sql >> Databasteknik >  >> RDS >> Sqlserver

Hämta kolumnnamn och typer av en lagrad procedur?

[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).



  1. SQLite-tabellbegränsning unik och ON CONFLICT REPLACE-användning

  2. SQLite FULL OUTER JOIN-emulering

  3. Hur skapar jag en dynamisk WHERE-sats?

  4. Varför ger mysqli ett kommandon osynkroniserat fel?