[Ik realiseer me net dat ik deze vraag al eerder heb beantwoord]
Dit doen voor een opgeslagen procedure is een stuk ingewikkelder dan voor een weergave of tabel. Een van de problemen is dat een opgeslagen procedure meerdere verschillende codepaden kan hebben, afhankelijk van invoerparameters en zelfs dingen die u niet kunt controleren, zoals serverstatus, tijd van de dag, enz. Dus wat u bijvoorbeeld zou verwachten als de uitvoer voor deze opgeslagen procedure? Wat als er meerdere resultatensets zijn, ongeacht de voorwaarden?
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
Als uw opgeslagen procedure geen codepaden heeft en u er zeker van bent dat u altijd dezelfde resultatenset zult zien (en vooraf kunt bepalen welke waarden moeten worden opgegeven als een opgeslagen procedure niet-optionele parameters heeft), laten we een eenvoudig voorbeeld nemen:
CREATE PROCEDURE dbo.bar
AS
BEGIN
SET NOCOUNT ON;
SELECT a = 'a', b = 1, c = GETDATE();
END
GO
FMTONLY
Een manier is om zoiets als dit te doen:
SET FMTONLY ON;
GO
EXEC dbo.bar;
Dit geeft u een lege resultatenset en uw clienttoepassing kan de eigenschappen van die resultatenset bekijken om kolomnamen en gegevenstypen te bepalen.
Nu zijn er veel problemen met SET FMTONLY ON;
waar ik hier niet op in zal gaan, maar er moet op zijn minst worden opgemerkt dat dit commando niet voor niets is afgekeurd. Zorg er ook voor dat SET FMTONLY OFF;
als u klaar bent, of u vraagt zich af waarom u een opgeslagen procedure met succes hebt gemaakt, maar deze vervolgens niet kunt uitvoeren. En nee, ik waarschuw je daar niet voor omdat het mij net is overkomen. Eerlijk. :-)
OPENVRAAG
Door een loopback-gekoppelde server te maken, kunt u tools gebruiken zoals OPENQUERY
om een opgeslagen procedure uit te voeren, maar een samenstelbare resultatenset terug te sturen (nou, accepteer dat als een extreem losse definitie) die u kunt inspecteren. Maak eerst een loopback-server (hierbij wordt uitgegaan van een lokale instantie met de naam 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 kunnen we de bovenstaande procedure volgen en deze in een query als deze invoeren:
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');
Dit negeert aliastypen (voorheen bekend als door de gebruiker gedefinieerde gegevenstypen) en kan ook twee rijen weergeven voor kolommen die zijn gedefinieerd als bijvoorbeeld sysname
. Maar van het bovenstaande levert het:
name name
---- --------
b int
c datetime
a varchar
Er is hier duidelijk meer werk te doen - varchar
laat geen lengte zien, en je zult precisie / schaal moeten krijgen voor andere typen zoals datetime2
, time
en decimal
. Maar dat is een begin.
SQL Server 2012
Er zijn enkele nieuwe functies in SQL Server 2012 die het ontdekken van metagegevens veel gemakkelijker maken. Voor de bovenstaande procedure kunnen we het volgende doen:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(
OBJECT_ID('dbo.bar'),
NULL
);
Dit zorgt onder andere voor precisie en schaal en lost aliastypen voor ons op. Voor de bovenstaande procedure levert dit:
name system_type_name
---- ----------------
a varchar(1)
b int
c datetime
Visueel is er niet veel verschil, maar als je begint met alle verschillende gegevenstypen met verschillende precisie en schaal, zul je het extra werk dat deze functie voor je doet op prijs stellen.
Het nadeel:in SQL Server 2012 werken deze functies tenminste alleen voor de eerste resultatenset (zoals de naam van de functie aangeeft).