sql >> Databasteknik >  >> RDS >> Sqlserver

Hämta kolumndefinition för resultatuppsättning för lagrad procedur

Så låt oss säga att du har en lagrad procedur i tempdb:

USE tempdb;
GO

CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
    SET NOCOUNT ON;

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

Det finns ett ganska invecklat sätt du kan gå tillväga för att bestämma metadata som den lagrade proceduren kommer att mata ut. Det finns flera förbehåll, inklusive proceduren kan bara mata ut en enda resultatuppsättning, och att en bästa gissning kommer att göras om datatypen om den inte kan fastställas exakt. Det kräver användning av OPENQUERY och en loopback länkad server med 'DATA ACCESS' egenskapen satt till true. Du kan kontrollera sys.servers för att se om du redan har en giltig server, men låt oss bara skapa en manuellt kallad loopback :

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

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

Nu när du kan fråga detta som en länkad server kan du använda resultatet av vilken fråga som helst (inklusive ett lagrat proceduranrop) som en vanlig SELECT . Så du kan göra detta (observera att databasprefixet är viktigt, annars får du fel 11529 och 2812):

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

Om vi ​​kan utföra en SELECT * , kan vi också utföra en SELECT * INTO :

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

Och när #tmp-tabellen väl finns, kan vi bestämma metadata genom att säga (om vi antar SQL Server 2005 eller senare):

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

(Om du använder SQL Server 2000 kan du göra något liknande med syskolumner, men jag har inte en 2000-instans till hands för att validera en motsvarande fråga.)

Resultat:

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

I Denali kommer detta att vara mycket, mycket, mycket lättare. Återigen finns det fortfarande en begränsning av den första resultatuppsättningen, men du behöver inte konfigurera en länkad server och hoppa igenom alla dessa ramar. Du kan bara säga:

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

Resultat:

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

Fram till Denali föreslår jag att det skulle vara lättare att bara kavla upp ärmarna och ta reda på datatyperna på egen hand. Inte bara för att det är tråkigt att gå igenom stegen ovan, utan också för att du är mycket mer benägen att göra en korrekt (eller åtminstone mer exakt) gissning än vad motorn kommer att göra, eftersom den datatyp som motorn gör kommer att baseras på körtid output, utan någon extern kunskap om domänen av möjliga värden. Denna faktor kommer att förbli sant i Denali också, så få inte intrycket av att de nya funktionerna för upptäckt av metadata är ett slut på allt, de gör bara ovanstående lite mindre tråkigt.

Åh och för några andra potentiella gotchas med OPENQUERY , se Erland Sommarskogs artikel här:

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



  1. Hur man skapar en användare med PSQL

  2. MyISAM kontra InnoDB

  3. T-SQL:Avrunda till närmaste 15 minuters intervall

  4. Problem med SQLiteOpenHelper på Android 2.X och 3.X