Dus laten we zeggen dat je een opgeslagen procedure hebt in tempdb:
USE tempdb;
GO
CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
SET NOCOUNT ON;
SELECT foo = 1, bar = 'tooth';
END
GO
Er is een nogal ingewikkelde manier om de metadata te bepalen die de opgeslagen procedure zal uitvoeren. Er zijn verschillende kanttekeningen, waaronder dat de procedure slechts één resultaatset kan opleveren, en dat er een beste schatting zal worden gemaakt over het gegevenstype als het niet precies kan worden bepaald. Het vereist het gebruik van OPENQUERY
en een loopback-gekoppelde server met de 'DATA ACCESS'
eigenschap ingesteld op true. Je kunt sys.servers controleren om te zien of je al een geldige server hebt, maar laten we er gewoon een handmatig maken met de naam loopback
:
EXEC master..sp_addlinkedserver
@server = 'loopback',
@srvproduct = '',
@provider = 'SQLNCLI',
@datasrc = @@SERVERNAME;
EXEC master..sp_serveroption
@server = 'loopback',
@optname = 'DATA ACCESS',
@optvalue = 'TRUE';
Nu u dit als gekoppelde server kunt opvragen, kunt u het resultaat van elke zoekopdracht (inclusief een opgeslagen procedureaanroep) gebruiken als een gewone SELECT
. U kunt dit dus doen (merk op dat het databasevoorvoegsel is belangrijk, anders krijg je fout 11529 en 2812):
SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
Als we een SELECT *
. kunnen uitvoeren , we kunnen ook een SELECT * INTO
. uitvoeren :
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
En zodra die #tmp-tabel bestaat, kunnen we de metadata bepalen door te zeggen (ervan uitgaande dat SQL Server 2005 of hoger):
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');
(Als u SQL Server 2000 gebruikt, kunt u iets soortgelijks doen met syscolumns, maar ik heb geen 2000-instantie bij de hand om een gelijkwaardige query te valideren.)
Resultaten:
name type max_length precision scale
--------- ------- ---------- --------- -----
foo int 4 10 0
bar varchar 5 0 0
In Denali zal dit veel, veel, veel gemakkelijker zijn. Nogmaals, er is nog steeds een beperking van de eerste resultatenset, maar je hoeft geen gekoppelde server op te zetten en door al die hoepels te springen. Je kunt gewoon zeggen:
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);
Resultaten:
name system_type_name
--------- ----------------
foo int
bar varchar(5)
Tot Denali stel ik voor dat het gemakkelijker zou zijn om gewoon je mouwen op te stropen en zelf de gegevenstypen te achterhalen. Niet alleen omdat het vervelend is om de bovenstaande stappen te doorlopen, maar ook omdat het veel waarschijnlijker is dat u een juiste (of op zijn minst nauwkeurigere) schatting maakt dan de engine zal doen, aangezien het gegevenstype dat de engine vermoedt gebaseerd is op runtime output, zonder enige externe kennis van het domein van mogelijke waarden. Deze factor blijft ook gelden in Denali, dus krijg niet de indruk dat de nieuwe functies voor het ontdekken van metadata alles zijn, ze maken het bovenstaande alleen wat minder vervelend.
Oh en voor een aantal andere mogelijke problemen met OPENQUERY
, zie hier het artikel van Erland Sommarskog:
http://www.sommarskog.se/share_data.html#OPENQUERY