2010-05-27 111 views
12

它看起來像通過EXECUTE字符串方法使用動態SQL創建的#temptables具有不同的作用域,並且不能在同一存儲過程中被「固定」SQL引用。 但是,我可以引用動態SQL語句在子序列動態SQL中創建的臨時表,但似乎存儲過程不會將查詢結果返回給調用客戶端,除非SQL已修復。T-SQL動態SQL和臨時表

一個簡單的2表方案: 我有2個表。我們稱之爲訂單和項目。 Order具有OrderId的主鍵,Items具有ItemId的主鍵。 Items.OrderId是標識父訂單的外鍵。一個訂單可以有1到n個項目。

我希望能夠爲用戶提供一個非常靈活的「查詢生成器」類型界面,以允許用戶選擇他想查看的項目。過濾條件可以基於Items表和/或Parent Order表中的字段。如果一個項目滿足包含父項目的條件的條件(如果存在),則該項目應該返回查詢以及父項目。

通常,我想大多數人會在Item表和父Order表之間構造一個連接。我想改爲執行2個單獨的查詢。一個返回所有合格項目,另一個返回所有不同的父訂單。原因有兩點,你可能會也可能不會同意。

第一個原因是我需要查詢父Order表中的所有列,並且如果我做了單個查詢以將Orders表連接到Items表,我將多次重新編制Order信息。由於每個訂單通常有大量的項目,我想避免這種情況,因爲這會導致更多的數據被轉移到胖客戶端。相反,如前所述,我想在數據集中分別返回兩個表,並使用其中的兩個表填充自定義Order和子項Items對象。 (我對LINQ或實體框架還不夠了解,我用手構建了我的對象)。第二個原因是我想返回兩個表而不是一個是因爲我已經有另一個過程返回給定OrderId的所有Items以及父Order,並且我想使用相同的2表方法,以便我可以重新使用客戶端代碼來填充返回的2個數據表中的自定義Order和Client對象。

我希望做的是這樣的:

建設上,通過對WinForm的創建自定義過濾器中指定其加入訂單表的項目表,每個表上適當的過濾器的客戶端動態SQL字符串胖客戶端應用程序。客戶端上的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個靜態SQL獲取,如果將靜態SQL更改爲動態,則不會將結果傳回胖客戶端。

3四處浮現在腦海中,但我是一個更好的看:

1)第一個SQL可以通過從客戶端執行動態構造SQL執行。然後結果可以作爲表格傳遞給上述存儲過程的修改版本。我熟悉將表格數據作爲XML傳遞。如果我這樣做了,那麼存儲過程可以使用靜態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) 

這仍然爲我提供了重用我的客戶面體羣的代碼,因爲訂單和項目繼續在兩個不同的表要返回的能力。

我有一種感覺,那我可能會用我的存儲過程中的表數據類型的一些選項,但是這也是新的給我,我將不勝感激勺子餵養的一點點在那一個。

如果您甚至遠遠在我寫掃描此,我很驚訝,但即便如此,我竟被dappreciate任何關於如何做到這一點最好你的看法。

+0

TLDR:http://www.urbandictionary.com/define.php?term = TLDR – 2010-05-27 00:26:59

回答

17

你需要創建表第一那麼它將在動態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. 選擇從臨時表

下面是完整的示例從動態SQL

create proc prTest2 @var varchar(100) 
as 
exec (@var) 
go 

create table #temp (id int) 

exec prTest2 'insert #temp values(1)' 

select * from #temp 
+0

我認爲這也適用:insert into #temptable exec('select ?? from ??'); – 2010-05-27 02:27:42

+4

問題是我們不知道列定義時,那麼呢? – Muflix 2017-02-20 15:09:01

0

結果集返回給客戶端。我做了很多。

你說得對與共享數據,通過臨時表和變量的問題以及諸如此類的SQL和它生成的動態SQL之間。

我覺得在試圖讓你的臨時表的工作,你可能已經得到了一些事情感到困惑,因爲你絕對可以從執行動態SQL一個SP獲得的數據:

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 
+0

如果你在臨時表中創建臨時表,它將不起作用,你需要先創建臨時表,然後你可以在臨時表中填充它。另請參見我的示例 – SQLMenace 2010-05-27 00:39:01

+0

@SQLMenace - 我明白你在說什麼。我的觀點是你可以從動態SQL返回集合(並且他們可以使用他們自己的臨時表並從它們返回)。我會添加第二個例子。 – 2010-05-27 04:15:00

2

我強烈建議你必須通過http://www.sommarskog.se/arrays-in-sql-2005.html

個人而言,我喜歡通過一個逗號分隔文本列表的方式讀取,然後收杆將它與文本添加到表函數並加入到它中。如果您在連接中首先創建臨時表方法,則該方法可以工作。但它感覺有點混亂。

+1

我會盡快通過XML而不是CSV。雖然更詳細,但它允許靈活地修改和傳遞其他列。而且SQL已經知道如何解析XML。但是我看到了將客戶端數據集傳遞給服務器端表變量的示例。很乾淨。即使如此,不如臨時表恕我直言,這是一種不太可能擴展的方法。 – ChadD 2010-05-27 03:02:11

3
DECLARE @DynamicQuery NVarchar(MAX) 
    Set @DynamicQuery='Select * into #temp from (select * from tablename) alias 
    select * from #temp 
    drop table #temp' 
    exec sp_executesql @DynamicQuery 

或第二種方法。
這將工作。但您需要特別注意全局變量。

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 
DROP Table ##temp2 

注意:如果你不知道數據庫的完整結構,不要使用此方法。