Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Recupera la definizione di colonna per il set di risultati della stored procedure

Quindi supponiamo che tu abbia una stored procedure in tempdb:

USE tempdb;
GO

CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
    SET NOCOUNT ON;

    SELECT foo = 1, bar = 'tooth';
END
GO

C'è un modo abbastanza contorto per determinare i metadati che la procedura memorizzata produrrà. Ci sono diversi avvertimenti, inclusa la procedura può produrre solo un singolo set di risultati e che verrà fatta un'ipotesi migliore sul tipo di dati se non può essere determinato con precisione. Richiede l'uso di OPENQUERY e un server collegato in loopback con il 'DATA ACCESS' proprietà impostata su true. Puoi controllare sys.servers per vedere se hai già un server valido, ma creiamone uno manualmente chiamato loopback :

EXEC master..sp_addlinkedserver 
    @server = 'loopback',  
    @srvproduct = '',
    @provider = 'SQLNCLI',
    @datasrc = @@SERVERNAME;

EXEC master..sp_serveroption 
    @server = 'loopback', 
    @optname = 'DATA ACCESS',
    @optvalue = 'TRUE';

Ora che puoi interrogare questo come un server collegato, puoi usare il risultato di qualsiasi query (inclusa una chiamata alla procedura memorizzata) come un normale SELECT . Quindi puoi farlo (nota che il prefisso del database è importante, altrimenti otterrai l'errore 11529 e 2812):

SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');

Se possiamo eseguire un SELECT * , possiamo anche eseguire un SELECT * INTO :

SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');

E una volta che la tabella #tmp esiste, possiamo determinare i metadati dicendo (supponendo SQL Server 2005 o versioni successive):

SELECT c.name, [type] = t.name, c.max_length, c.[precision], c.scale
  FROM sys.columns AS c
  INNER JOIN sys.types AS t
  ON c.system_type_id = t.system_type_id
  AND c.user_type_id = t.user_type_id
  WHERE c.[object_id] = OBJECT_ID('tempdb..#tmp');

(Se stai usando SQL Server 2000, puoi fare qualcosa di simile con syscolumns, ma non ho un'istanza 2000 a portata di mano per convalidare una query equivalente.)

Risultati:

name      type    max_length precision scale
--------- ------- ---------- --------- -----
foo       int              4        10     0
bar       varchar          5         0     0

In Denali, sarà molto, molto, molto più facile. Ancora una volta c'è ancora una limitazione del primo set di risultati, ma non è necessario configurare un server collegato e saltare attraverso tutti quei cerchi. Puoi semplicemente dire:

DECLARE @sql NVARCHAR(MAX) = N'EXEC tempdb.dbo.my_procedure;';

SELECT name, system_type_name
    FROM sys.dm_exec_describe_first_result_set(@sql, NULL, 1);

Risultati:

name      system_type_name
--------- ----------------
foo       int             
bar       varchar(5)      

Fino a Denali, suggerisco che sarebbe più facile rimboccarsi le maniche e capire da soli i tipi di dati. Non solo perché è noioso eseguire i passaggi precedenti, ma anche perché è molto più probabile che tu faccia un'ipotesi corretta (o almeno più accurata) di quella che farà il motore, poiché il tipo di dati che il motore effettua sarà basato sul runtime output, senza alcuna conoscenza esterna del dominio dei valori possibili. Questo fattore rimarrà valido anche in Denali, quindi non avere l'impressione che le nuove funzionalità di rilevamento dei metadati siano un punto fermo, rendono solo quanto sopra un po' meno noioso.

Oh, e per altri potenziali trucchi con OPENQUERY , vedi l'articolo di Erland Sommarskog qui:

http://www.sommarskog.se/share_data.html#OPENQUERY