T-SQL динамические таблицы SQL и Temp

похоже, что # temptables, созданные с помощью динамического SQL с помощью метода EXECUTE string, имеют другую область и не могут ссылаться на" фиксированные " SQLs в той же хранимой процедуре. Однако я могу ссылаться на временную таблицу, созданную динамической инструкцией SQL в динамическом SQL подпоследовательности, но кажется, что хранимая процедура не возвращает результат запроса вызывающему клиенту, если SQL не исправлен.

простой сценарий таблицы 2: У меня 2 стола. Назовем их заказами и предметами. Заказ имеет первичный ключ OrderId, а элементы-первичный ключ ItemId. Предметы.OrderId-это внешний ключ для идентификации родительского заказа. Заказ может иметь от 1 до n элементов.

Я хочу иметь возможность предоставить пользователю очень гибкий интерфейс типа "построитель запросов", чтобы позволить пользователю выбирать, какие элементы он хочет видеть. Критерии фильтра могут основываться на полях таблицы номенклатуры и / или таблицы родительского заказа. Если элемент соответствует условию фильтра, включая и условие на Родительский порядок если он существует, элемент должен быть возвращен в запросе, а также родительский порядок.

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

первая причина в том, что мне нужно запросить все столбцы в родительской таблице заказов, и если бы я сделал один запрос, чтобы соединить таблицу заказов с таблицей элементов, я бы повторно использовал информацию о заказе несколько раз. Поскольку в каждом заказе обычно большое количество элементов, я бы хотел избежать этого, потому что это приведет к гораздо большему количеству данных, передаваемых жирному клиенту. Вместо этого, как уже упоминалось, я хотел бы вернуть две таблицы по отдельности в dataset и использовать две таблицы внутри для заполнения клиента пользовательского порядка и дочерних элементов объекты. (Я еще недостаточно знаю о LINQ или Entity Framework. Я строю свои объекты вручную). Вторая причина, по которой я хотел бы вернуть две таблицы вместо одной, заключается в том, что у меня уже есть другая процедура, которая возвращает все элементы для данного OrderId вместе с родительским порядком, и я хотел бы использовать тот же 2-табличный подход, чтобы я мог повторно использовать клиентский код для заполнения моего пользовательского порядка и клиентских объектов из 2 возвращаемых datatables.

что я надеялся сделать это:

создайте динамическую строку SQL на клиенте, которая присоединяет таблицу orders к таблице Items и фильтрует соответствующие таблицы в соответствии с пользовательским фильтром, созданным в приложении WinForm fat-client. Сборка SQL на клиенте выглядела бы примерно так:

TempSQL = "

    INSERT INTO #ItemsToQuery
       OrderId, ItemsId
    FROM
       Orders, Items 
    WHERE
       Orders.OrderID = Items.OrderId AND
       /* Some unpredictable Order filters go here */
      AND
       /* Some unpredictable Items filters go here */
    "

тогда я бы назвал хранимую процедуру,

CREATE PROCEDURE GetItemsAndOrders(@tempSql as text)
   Execute (@tempSQL) --to create the #ItemsToQuery table

SELECT * FROM Items WHERE Items.ItemId IN (SELECT ItemId FROM #ItemsToQuery)

SELECT * FROM Orders WHERE Orders.OrderId IN (SELECT DISTINCT OrderId FROM #ItemsToQuery)

проблема с этим подходом заключается в том, что таблица #ItemsToQuery, поскольку она была создана динамическим SQL, недоступно из следующих 2 статических SQLs, и если я изменяю статические SQLs на динамические, результаты не передаются обратно клиенту fat.

3 вокруг приходят на ум, но я ищу лучший:

1) первый SQL может быть выполнен путем выполнения динамически построенного SQL от клиента. Затем результаты могут быть переданы в виде таблицы в измененную версию вышеуказанной хранимой процедуры. Я знаком с передачей табличных данных в формате XML. Если я это сделал, сохраненный proc затем можно вставить данные во временную таблицу с помощью статического SQL, который, поскольку он был создан динамическим SQL, может быть запрошен без проблем. (Я также мог бы исследовать передачу нового типа таблицы param вместо XML.) Однако я хотел бы избежать передачи потенциально больших списков в хранимую процедуру.

2) я могу выполнить все запросы от клиента.

первое было бы примерно так:

SELECT Items.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
SELECT Orders.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)

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

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

Если вы даже сканировали это далеко в том, что я написал, Я удивлен, но если это так, я бы оценил любые ваши мысли о том, как сделать это лучше.

5 ответов


сначала вам нужно сначала создать таблицу, затем она будет доступна в динамическом SQL.

это работает:

CREATE TABLE #temp3 (id INT)
EXEC ('insert #temp3 values(1)')

SELECT *
FROM #temp3

Это не сработает:

EXEC (
        'create table #temp2 (id int)
         insert #temp2 values(1)'
        )

SELECT *
FROM #temp2

другими словами:

  1. создать временную таблицу
  2. выполнить proc
  3. выберите из таблицы temp

вот пример:

CREATE PROC prTest2 @var VARCHAR(100)
AS
EXEC (@var)
GO

CREATE TABLE #temp (id INT)

EXEC prTest2 'insert #temp values(1)'

SELECT *
FROM #temp

1-й метод-заключите несколько операторов в один и тот же динамический вызов SQL:

DECLARE @DynamicQuery NVARCHAR(MAX)

SET @DynamicQuery = 'Select * into #temp from (select * from tablename) alias 
select * from #temp
drop table #temp'

EXEC sp_executesql @DynamicQuery

2-й метод - используйте глобальную таблицу Temp:
(осторожно, вам нужно позаботиться о глобальной переменной.)

IF OBJECT_ID('tempdb..##temp2') IS NULL
BEGIN
    EXEC (
            'create table ##temp2 (id int)
             insert ##temp2 values(1)'
            )

    SELECT *
    FROM ##temp2
END

не забудьте удалить объект # # temp2 вручную, как только вы закончите с ним:

IF (OBJECT_ID('tempdb..##temp2') IS NOT NULL)
BEGIN
     DROP Table ##temp2
END

Примечание: не используйте этот метод 2, Если вы не знаете полную структуру базы данных.


Я настоятельно рекомендую вам прочитать http://www.sommarskog.se/arrays-in-sql-2005.html

лично мне нравится подход передачи списка текста с разделителями-запятыми, а затем его разбора с помощью функции text to table и присоединения к нему. Подход к временной таблице может работать, если вы создадите его первым в соединении. Но это немного сложнее.


результирующие наборы из динамического SQL возвращаются клиенту. Я делал это довольно много.

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

Я думаю, что, пытаясь заставить вашу временную таблицу работать, вы, вероятно, что-то перепутали, потому что вы можете определенно получить данные от SP, который выполняет динамический В SQL:

USE SandBox
GO

CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS 
BEGIN
    DECLARE @sql AS VARCHAR(MAX) = 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + ''''
    EXEC (@sql)
END
GO

EXEC usp_DynTest 'BASE TABLE'
GO

EXEC usp_DynTest 'VIEW'
GO

DROP PROCEDURE usp_DynTest
GO

также:

USE SandBox
GO

CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS 
BEGIN
    DECLARE @sql AS VARCHAR(MAX) = 'SELECT * INTO #temp FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + '''; SELECT * FROM #temp;'
    EXEC (@sql)
END
GO

EXEC usp_DynTest 'BASE TABLE'
GO

EXEC usp_DynTest 'VIEW'
GO

DROP PROCEDURE usp_DynTest
GO

У меня была та же проблема, что и @Muflix. Когда вы не знаете возвращаемые столбцы или они генерируются динамически, я создал глобальную таблицу с уникальным идентификатором, а затем удалил ее, когда я закончил с ней, это выглядит примерно так, как показано ниже:

DECLARE @DynamicSQL NVARCHAR(MAX)
DECLARE @DynamicTable VARCHAR(255) = 'DynamicTempTable_' + CONVERT(VARCHAR(36), NEWID())
DECLARE @DynamicColumns NVARCHAR(MAX)

--Get "@DynamicColumns", example: SET @DynamicColumns = '[Column1], [Column2]'

SET @DynamicSQL = 'SELECT ' + @DynamicColumns + ' INTO [##' + @DynamicTable + ']' + 
     ' FROM [dbo].[TableXYZ]'

EXEC sp_executesql @DynamicSQL

SET @DynamicSQL = 'IF OBJECT_ID(''tempdb..##' + @DynamicTable + ''' , ''U'') IS NOT NULL ' + 
    ' BEGIN DROP TABLE [##' + @DynamicTable + '] END'

EXEC sp_executesql @DynamicSQL

конечно, не самое лучшее решение, но это, кажется, работает для меня.