Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Recuperare i nomi di colonna e i tipi di una stored procedure?

[Mi sono appena reso conto di aver già risposto a questa domanda]

Questa operazione per una stored procedure è molto più complicata di quanto non lo sia per una vista o una tabella. Uno dei problemi è che una procedura memorizzata può avere più percorsi di codice diversi a seconda dei parametri di input e persino di cose che non puoi controllare come lo stato del server, l'ora del giorno, ecc. Quindi, ad esempio, cosa ti aspetteresti di vedere come output per questa procedura memorizzata? Cosa succede se sono presenti più set di risultati indipendentemente dai condizionali?

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

Se la tua procedura memorizzata non ha percorsi di codice e sei sicuro che vedrai sempre lo stesso set di risultati (e puoi determinare in anticipo quali valori dovrebbero essere forniti se una procedura memorizzata ha parametri non opzionali), facciamo un semplice esempio:

CREATE PROCEDURE dbo.bar
AS
BEGIN
  SET NOCOUNT ON;

  SELECT a = 'a', b = 1, c = GETDATE();
END
GO

FMTONLY

Un modo è fare qualcosa del genere:

SET FMTONLY ON;
GO
EXEC dbo.bar;

Questo ti darà un set di risultati vuoto e la tua applicazione client può dare un'occhiata alle proprietà di quel set di risultati per determinare i nomi delle colonne e i tipi di dati.

Ora, ci sono molti problemi con SET FMTONLY ON; che non entrerò qui, ma almeno va notato che questo comando è deprecato, per una buona ragione. Fai attenzione anche a SET FMTONLY OFF; quando hai finito, o ti chiederai perché crei una stored procedure con successo ma poi non riesci a eseguirla. E no, non te lo sto avvertendo perché è successo solo a me. Onesto. :-)

OPENQUERY

Creando un server collegato in loopback, puoi quindi utilizzare strumenti come OPENQUERY per eseguire una procedura memorizzata ma restituire un set di risultati componibile (beh, accettalo come una definizione estremamente vaga) che puoi ispezionare. Per prima cosa crea un server di loopback (questo presuppone un'istanza locale denominata 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';

Ora possiamo seguire la procedura sopra e inserirla in una query come questa:

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

Questo ignora i tipi di alias (precedentemente noti come tipi di dati definiti dall'utente) e può anche mostrare due righe per le colonne definite come, ad esempio, sysname . Ma da quanto sopra produce:

name   name
----   --------
b      int
c      datetime
a      varchar

Ovviamente c'è più lavoro da fare qui - varchar non mostra la lunghezza e dovrai ottenere precisione/scala per altri tipi come datetime2 , time e decimal . Ma questo è un inizio.

SQL Server 2012

Sono disponibili alcune nuove funzioni in SQL Server 2012 che semplificano notevolmente l'individuazione dei metadati. Per la procedura di cui sopra possiamo fare quanto segue:

SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(
  OBJECT_ID('dbo.bar'), 
  NULL
);

Tra le altre cose, questo fornisce effettivamente precisione e scala e risolve i tipi di alias per noi. Per la procedura di cui sopra questo produce:

name   system_type_name
----   ----------------
a      varchar(1)
b      int
c      datetime

Non c'è molta differenza visivamente, ma quando inizi a entrare in tutti i diversi tipi di dati con precisione e scala diverse, apprezzerai il lavoro extra che questa funzione fa per te.

Lo svantaggio:in SQL Server 2012 almeno queste funzioni funzionano solo per il primo set di risultati (come suggerisce il nome della funzione).