Как получить доступ к набору данных в текущей области, сгенерированным при вызове хранимой процедуры в TSQL? - PullRequest
7 голосов
/ 02 июня 2011

Проблемный фон

Создать и получить доступ к данным фиксированной структуры столбцов легко. Вы можете заранее создать локальные временные таблицы и заполнить их, вызвав хранимые процедуры.

С другой стороны, если вы хотите сгенерировать данные с динамической разметкой столбцов, вы, как правило, должны динамически создавать оператор SQL и выполнять его с помощью «exec sp_executesql». Поскольку компоновка данных неизвестна во время выполнения, вы не можете создать временную таблицу заранее, и однажды внутри оператора "exec sp_executesql" все временные таблицы, созданные там, привязываются к этой области и исчезают при возврате вызова, поэтому гораздо сложнее получить доступ к данным (т. е. ваши возможности более ограничены).

Моя конкретная ситуация

У меня есть запрос для доступа к данным в динамически генерируемой таблице.

Таблица генерируется хранимой процедурой, которая динамически создает запрос, сохраняет его в переменной "@sql nvarchar (max)" и запускает его, вызывая "exec sp_executesql @statement = @sql".

Оператор @sql был чем-то вроде «select * into #temptable from ...», но #temptable был уничтожен ко времени возврата «exec sp_executesql». Быстрое решение этой проблемы состояло в том, чтобы вместо этого просто использовать «## temptable» (то есть глобальную временную таблицу), потому что она сохраняется, когда хранимая процедура возвращает И я могу легко получить к ней доступ в вызывающей области (поскольку она имеет известное / статическое имя ).

Мне не нравится это решение, потому что глобальные временные таблицы не являются поточно-ориентированными (с точки зрения сопоставления имен), и я не хочу связываться с динамически генерируемыми уникальными именами, потому что я просто закончу необходимость использовать более динамический SQL для доступа к ним ... что возвращает меня к исходной точке, оставляя данные недоступными вне SP.

Я не думаю, что возвращение табличных переменных (через выходные параметры) является опцией (также новой для SQL Server 2008), если это не может быть сделано без определения статического типа таблицы. Таблицы, которые генерирует моя хранимая процедура, являются динамическими и зависят от переданных входных параметров.

Встроенные табличные функции не являются опцией, потому что я запускаю циклы кода для построения запроса @sql и вызываю "exec sp_executesql".

Табличные функции с несколькими операторами (вместо хранимой процедуры), также не вариант, потому что такая функция должна иметь четко определенный формат таблицы, в то время как я запускаю dyanmic SQL, чтобы вернуть таблицу с переменное количество столбцов и имен столбцов в зависимости от значений входных параметров.

Все, что я действительно хочу сделать, это выбрать результирующий набор динамического запроса в новую таблицу, но я нахожу это трудным, поскольку ни один из вышеперечисленных способов не работает; Особенно раздражает то, что локальные временные таблицы не являются локальными для сеанса, а локальными для хранимой процедуры, поэтому они возвращаются при возврате. Единственное решение , которое я видел, настаивает на том, что использование OPENROWSET является единственным способом, но я не хочу связываться со строками соединения внутри моей хранимой процедуры, по той же причине, по которой я не хочу включать уникальные -имен-код управления ... это просто намного сложнее, чем должно быть.

Итак, я просто хочу выполнить динамический SQL, который генерирует набор данных неизвестного формата, и иметь возможность легко получить к нему доступ из вызывающей области.

Ответы [ 3 ]

1 голос
/ 12 августа 2011

Один неуклюжий, но возможный вариант - создать таблицу с одним столбцом в вызывающей стороне, а затем изменить ее в вызываемой. Это решает проблему объема, но усложняет ситуацию ...

Если вы создаете хранимую процедуру, которая принимает имена входных и выходных таблиц, при которой вторая таблица выглядит так же, как и первая, при удалении и добавлении столбцов у вас может быть более общее решение.

Выбор из tempdb.INFORMATION_SCHEMA.COLUMNS позволит вам найти информацию столбца о любой временной таблице (которую можно определить по тому, начинается ли имя таблицы с #). Вот пример:

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 или * 1016. *, затем скопируйте структуру таблицы в правильную структуру с помощью SP, копирующего таблицу, затем запустите процесс обработки данных для реального добавления только один раз в правильную таблицу.

Как я уже сказал, неуклюжий, но может быть полезен в некоторых узких ситуациях, когда другие варианты недоступны.

1 голос
/ 18 августа 2014

Итак, я копирую свой ответ из Вставьте результаты хранимой процедуры во временную таблицу . Надеюсь, поможет. Да, это снова глобальная временная таблица, и единственное отличие - добавленный туда 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 + ']')
1 голос
/ 02 июня 2011

Создайте временную таблицу до sp_executesql: она все еще будет находиться в области видимости для "внутренних" областей, таких как sp_executesql

Измените SQL на INSERT вместо SELECT..INTO...

Edit:

Сделать таблицу достаточно широкой, чтобы охватить все варианты.

Честно говоря, SQL предназначен для работы с фиксированными определениями таблиц: переменные выходные сигнатуры (таблицы) приводят к вашей проблеме ...

...