Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Rufen Sie die Spaltendefinition für die Ergebnismenge der gespeicherten Prozedur ab

Nehmen wir also an, Sie haben eine gespeicherte Prozedur in tempdb:

USE tempdb;
GO

CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
    SET NOCOUNT ON;

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

Es gibt eine ziemlich verworrene Art und Weise, wie Sie die Metadaten bestimmen können, die die gespeicherte Prozedur ausgeben wird. Es gibt mehrere Vorbehalte, darunter, dass die Prozedur nur einen einzigen Ergebnissatz ausgeben kann und dass der Datentyp bestmöglich geschätzt wird, wenn er nicht genau bestimmt werden kann. Es erfordert die Verwendung von OPENQUERY und ein Loopback-Verbindungsserver mit dem 'DATA ACCESS' Eigenschaft auf true gesetzt. Sie können sys.servers überprüfen, um zu sehen, ob Sie bereits einen gültigen Server haben, aber erstellen wir einfach manuell einen namens loopback :

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

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

Da Sie dies nun als Verbindungsserver abfragen können, können Sie das Ergebnis einer beliebigen Abfrage (einschließlich eines Aufrufs einer gespeicherten Prozedur) als reguläres SELECT verwenden . Sie können dies also tun (beachten Sie, dass das Datenbankpräfix ist wichtig, sonst erhalten Sie Fehler 11529 und 2812):

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

Wenn wir ein SELECT * ausführen können , können wir auch ein SELECT * INTO ausführen :

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

Und sobald diese #tmp-Tabelle existiert, können wir die Metadaten ermitteln, indem wir sagen (vorausgesetzt, SQL Server 2005 oder höher):

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');

(Wenn Sie SQL Server 2000 verwenden, können Sie etwas Ähnliches mit syscolumns machen, aber ich habe keine 2000-Instanz zur Hand, um eine äquivalente Abfrage zu validieren.)

Ergebnisse:

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

In Denali wird dies viel, viel, viel einfacher sein. Auch hier gibt es immer noch eine Einschränkung der ersten Ergebnismenge, aber Sie müssen keinen Verbindungsserver einrichten und all diese Hürden überwinden. Sie können einfach sagen:

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);

Ergebnisse:

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

Bis Denali schlage ich vor, dass es einfacher wäre, einfach die Ärmel hochzukrempeln und die Datentypen selbst herauszufinden. Nicht nur, weil es mühsam ist, die obigen Schritte durchzugehen, sondern auch, weil Sie viel wahrscheinlicher eine korrekte (oder zumindest genauere) Vermutung treffen als die Engine, da die Vermutungen des Datentyps, die die Engine macht, auf der Laufzeit basieren Ausgabe, ohne externes Wissen über den Bereich möglicher Werte. Dieser Faktor wird auch für Denali gelten, also erwecken Sie nicht den Eindruck, dass die neuen Metadaten-Discovery-Funktionen das A und O sind, sie machen das Obige nur ein bisschen weniger langweilig.

Oh, und für einige andere potenzielle Fallstricke mit OPENQUERY , siehe Artikel von Erland Sommarskog hier:

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