Как получить доступ к набору данных в текущей области, сгенерированной вызовом хранимой процедуры в TSQL?
Проблема фона
Простое создание и доступ к данным с фиксированным расположением столбцов. Вы можете создавать локальные таблицы temp вверх и заполнять их, вызывая хранимые процедуры.
С другой стороны, если вы хотите генерировать данные с динамическим расположением столбцов, вы должны, как правило, строить инструкцию SQL динамически и выполнять ее с помощью "exec sp_executesql" . Поскольку макет данных неизвестен во время выполнения, вы не можете создать временную таблицу вверх и один раз внутри оператора exec sp_executesql, любые временные таблицы, созданные там, привязаны к этой области и исчезают, когда вызов возвращается, поэтому гораздо труднее получить доступ к данным (т.е. ваши варианты более ограничены).
Моя специфическая ситуация
У меня есть запрос, которому необходимо получить доступ к данным в динамически сгенерированной таблице.
Таблица генерируется хранимой процедурой, которая динамически создает запрос, сохраняет его в переменной "@sql nvarchar (max)" и запускает ее, вызывая "exec sp_executesql @statement = @sql".
Оператор @sql был чем-то вроде "select * in #temptable from...", но #temptable был уничтожен к моменту возврата "exec sp_executesql" . Быстрое исправление заключается в том, чтобы вместо этого использовать "## temptable" (т.е. Глобальную таблицу temp), поскольку она сохраняется, когда возвращается хранимая процедура, и я могу легко получить к ней доступ в области вызова (поскольку у нее есть известное/статическое имя).
Мне не нравится это решение, потому что глобальные таблицы temp не являются потокобезопасными (имя collistion-wise), и я не хочу связываться с динамически генерируемыми уникальными именами, потому что я просто закончу чтобы использовать более динамический SQL для доступа к ним... который возвращает меня обратно на квадрат, оставляя данные недоступными вне SP.
Я не думаю, что возвращаемые переменные таблицы (через выходные параметры) - это опция (новая для SQL Server 2008), если только это не может быть сделано без необходимости определения типа статической таблицы. Таблицы, созданные моей хранимой процедурой, являются динамическими и зависят от переданных входных параметров.
Встроенные функции, основанные на таблице, не являются опцией, потому что я запускаю циклы кода для построения запроса @sql и вызова "exec sp_executesql" .
Многозначные табличные функции (вместо хранимой процедуры) также не являются опцией, поскольку такая функция должна иметь четко определенный формат таблицы, тогда как я запускаю dyanmic SQL для возврата таблицы с переменное количество столбцов и имен столбцов в зависимости от значений входных параметров.
Все, что я действительно хочу сделать, это выбрать набор результатов динамического запроса в новую таблицу, но мне сложно, поскольку ни одно из вышеизложенных не работает; особенно раздражает то, как локальные временные таблицы не являются локальными для сеанса, но локальны для хранимой процедуры, чтобы они исчезали при возврате. Единственное решение, которое я видел, настаивает на том, что использование OPENROWSET - единственный способ, но я не хочу связываться со строками подключения внутри моей хранимой процедуры по той же причине Я не хочу включать код управления уникальным именем... он просто более сложный, чем он должен быть.
В общем, я просто хочу выполнить динамический SQL, который генерирует набор данных неизвестного формата и сможет легко получить к нему доступ из области вызова.
Ответы
Ответ 1
Создайте временную таблицу перед sp_executesql
: она по-прежнему будет доступна для "внутренних" областей, таких как sp_executesql
Измените SQL, чтобы сделать INSERT
, а не SELECT..INTO...
Edit:
Сделайте таблицу достаточно широкой, чтобы охватить все параметры.
Откровенно говоря, SQL предназначен для работы с фиксированными определениями таблиц: переменные выходные сигнатуры (таблицы) приводят к проблеме, которая у вас есть...
Ответ 2
Один неуклюжий, но возможный вариант - создать в вызывающем абоненте таблицу с одним столбцом, а затем изменить ее в вызываемом. Это касается проблемы области, но делает вещи немного сложнее...
Если вы создаете хранимая процедура, которая принимает имена входных и выходных таблиц, что делает вторую таблицу одинаковой, как первая, путем добавления и добавления столбцов, вы можете иметь более общее решение.
Выбор из tempdb.INFORMATION_SCHEMA.COLUMNS
позволит вам найти информацию о столбцах о любой таблице temp (которую вы можете определить, начиная с имени таблицы с #
). Вот пример:
CREATE TABLE #blah (a int)
SELECT *
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE Object_id('tempdb.dbo.' + TABLE_NAME) = Object_id('tempdb.dbo.#blah')
DROP TABLE #blah
Обратите внимание, что имя таблицы в представлении не равно #blah
(вероятно, это будет что-то вроде #blah___{lots of underscores}___00000000021D
, поэтому использование Object_id()
для корреляции двух.
Чтобы использовать его практически, вместо заполнения первой таблицы данными, затем, изменив вторую таблицу и скопировав в нее данные, я предложил бы сначала создать пустую таблицу, запустив ваш процесс с добавленным TOP 0
или WHERE 1 = 0
, а затем скопируйте структуру таблицы в правильную с помощью вашего табличного копирования SP, а затем запустите процесс данных для реального, чтобы вставить только один раз в правильную таблицу.
Как я уже сказал, он неуклюж, но может быть полезен в некоторых узких ситуациях, когда нет других вариантов.
Ответ 3
Итак, я копирую свой ответ из Вставить результаты хранимой процедуры во временную таблицу. Надеюсь, поможет. Да, это глобальная временная таблица снова, и единственным отличием является GUID, добавленный там.
Я встретил ту же проблему, и вот что я сделал для этого из предложение Павла. Основная часть здесь заключается в использовании NEWID()
, чтобы избежать одновременного запуска нескольких процедур хранения/сценариев, боль для глобальной временной таблицы.
DECLARE @sql varchar(max) = '',
@tmp_global_table varchar(255) = '##global_tmp_' + CONVERT(varchar(36), NEWID())
SET @sql = @sql + 'select * into [' + @tmp_global_table + '] from YOURTABLE'
EXEC(@sql)
EXEC('SELECT * FROM [' + @tmp_global_table + ']')