Допустим, у вас есть хранимая процедура в базе данных tempdb:
USE tempdb;
GO
CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
SET NOCOUNT ON;
SELECT foo = 1, bar = 'tooth';
END
GO
Существует довольно запутанный способ определения метаданных, которые выдает хранимая процедура. Существует несколько предостережений, в том числе процедура может выводить только один результирующий набор, и о том, что тип данных будет лучше всего определен, если он не может быть точно определен. Это требует использования OPENQUERY
и сервера с обратной связью со свойством 'DATA ACCESS'
, установленным в true. Вы можете проверить sys.servers, чтобы убедиться, что у вас уже есть действительный сервер, но давайте просто создадим один из них, называемый loopback
:
EXEC master..sp_addlinkedserver
@server = 'loopback',
@srvproduct = '',
@provider = 'SQLNCLI',
@datasrc = @@SERVERNAME;
EXEC master..sp_serveroption
@server = 'loopback',
@optname = 'DATA ACCESS',
@optvalue = 'TRUE';
Теперь, когда вы можете запросить это как связанный сервер, вы можете использовать результат любого запроса (включая вызов хранимой процедуры) как обычный SELECT
. Таким образом, вы можете сделать это (обратите внимание, что префикс базы данных важен , в противном случае вы получите ошибки 11529 и 2812):
SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
Если мы можем выполнить SELECT *
, мы также можем выполнить SELECT * INTO
:
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
И как только эта таблица #tmp существует, мы можем определить метаданные, сказав (при условии, что SQL Server 2005 или выше):
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');
(Если вы используете SQL Server 2000, вы можете сделать что-то подобное с syscolumns, но у меня нет экземпляра 2000, удобного для проверки эквивалентного запроса.)
Результаты:
name type max_length precision scale
--------- ------- ---------- --------- -----
foo int 4 10 0
bar varchar 5 0 0
В Денали это будет намного, намного, намного проще. Опять же, существует ограничение первого набора результатов, но вам не нужно настраивать связанный сервер и перепрыгивать через все эти циклы. Вы можете просто сказать:
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);
Результаты:
name system_type_name
--------- ----------------
foo int
bar varchar(5)
До Денали, я полагаю, было бы проще просто засучить рукава и выяснить типы данных самостоятельно. Не только потому, что выполнять вышеуказанные шаги утомительно, но и потому, что вы с гораздо большей вероятностью сделаете правильное (или, по крайней мере, более точное) предположение, чем будет делать движок, так как тип данных предполагает, что движок будет основан на времени выполнения. вывод, без каких-либо внешних знаний о области возможных значений. Этот фактор останется верным и в Denali, так что не создавайте впечатление, что новые функции обнаружения метаданных являются конечной целью, они просто делают вышеупомянутое менее утомительным.
Да, и о некоторых других потенциальных ошибках с OPENQUERY
, см. Статью Эрланда Соммарскога здесь:
http://www.sommarskog.se/share_data.html#OPENQUERY