[Jag insåg precis att jag har svarat på den här frågan förut]
Att göra detta för en lagrad procedur är mycket mer komplicerat än det är för en vy eller tabell. Ett av problemen är att en lagrad procedur kan ha flera olika kodvägar beroende på ingångsparametrar och till och med saker du inte kan kontrollera som servertillstånd, tid på dygnet etc. Så till exempel vad du kan förvänta dig att se som utdata för denna lagrade procedur? Vad händer om det finns flera resultatuppsättningar oavsett villkor?
CREATE PROCEDURE dbo.foo
@bar INT
AS
BEGIN
SET NOCOUNT ON;
IF @bar = 1
SELECT a, b, c FROM dbo.blat;
ELSE
SELECT d, e, f, g, h FROM dbo.splunge;
END
GO
Om din lagrade procedur inte har kodsökvägar och du är säker på att du alltid kommer att se samma resultatuppsättning (och kan bestämma i förväg vilka värden som ska anges om en lagrad procedur har icke-valfria parametrar), låt oss ta ett enkelt exempel:
CREATE PROCEDURE dbo.bar
AS
BEGIN
SET NOCOUNT ON;
SELECT a = 'a', b = 1, c = GETDATE();
END
GO
FMTONLY
Ett sätt är att göra något så här:
SET FMTONLY ON;
GO
EXEC dbo.bar;
Detta ger dig en tom resultatuppsättning och din klientapplikation kan ta en titt på egenskaperna för den resultatuppsättningen för att bestämma kolumnnamn och datatyper.
Nu finns det många problem med SET FMTONLY ON;
som jag inte kommer att gå in på här, men det bör åtminstone noteras att detta kommando är föråldrat - av goda skäl. Var också noga med att SET FMTONLY OFF;
när du är klar, eller så kommer du att undra varför du skapar en lagrad procedur framgångsrikt men sedan inte kan köra den. Och nej, jag varnar dig inte för det eftersom det bara hände mig. Ärliga. :-)
OPENQUERY
Genom att skapa en loopback länkad server kan du sedan använda verktyg som OPENQUERY
att köra en lagrad procedur men returnera en komponerbar resultatuppsättning (nåja, acceptera det som en extremt lös definition) som du kan inspektera. Skapa först en loopback-server (detta förutsätter en lokal instans som heter FOO
):
USE master;
GO
EXEC sp_addlinkedserver @server = N'.\FOO', @srvproduct=N'SQL Server'
GO
EXEC sp_serveroption @server=N'.\FOO', @optname=N'data access',
@optvalue=N'true';
Nu kan vi ta proceduren ovan och mata in den i en fråga så här:
SELECT * INTO #t
FROM OPENQUERY([.\FOO], 'EXEC dbname.dbo.bar;')
WHERE 1 = 0;
SELECT c.name, t.name
FROM tempdb.sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#t');
Detta ignorerar aliastyper (tidigare kända som användardefinierade datatyper) och kan även visa två rader för kolumner definierade som till exempel sysname
. Men från ovanstående producerar den:
name name
---- --------
b int
c datetime
a varchar
Uppenbarligen finns det mer arbete att göra här - varchar
visar inte längd, och du måste få precision/skala för andra typer som datetime2
, time
och decimal
. Men det är en början.
SQL Server 2012
Det finns några nya funktioner i SQL Server 2012 som gör det mycket lättare att hitta metadata. För proceduren ovan kan vi göra följande:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(
OBJECT_ID('dbo.bar'),
NULL
);
Bland annat ger detta faktiskt precision och skala och löser aliastyper för oss. För proceduren ovan ger detta:
name system_type_name
---- ----------------
a varchar(1)
b int
c datetime
Inte mycket skillnad visuellt men när du börjar komma in i alla olika datatyper med olika precision och skala kommer du att uppskatta det extra arbete som denna funktion gör för dig.
Nackdelen:I SQL Server 2012 fungerar åtminstone dessa funktioner bara för den första resultset (som namnet på funktionen antyder).