2015-04-28 81 views
1

我想使用一些動態SELECT語句。表或列的名稱可能是不同的,這就是爲什麼我使用動態存儲過程是這樣的:SQL Server:動態函數或重用存儲過程

CREATE PROCEDURE [dbo].[test_sp](
    @database nvarchar(70)= '' 
    , @table_name nvarchar(70)= '' 
    , @column nvarchar(70)= '' 
) 
AS 
    DECLARE @sql nvarchar(max)= 'SELECT ' + @column + 
           'FROM [' + @database + 
           '].[dbo].[' + @table_name + '] ' 

    EXEC(@sql); 
GO 

這工作得很好。現在我得到越來越多的存儲過程與類似的代碼片段。關於維護,我想重複使用片段或使用動態功能。

一個例子:我有另一個存儲過程應「過濾器」的結果集的此呈現的示例(一個額外的要求是不添加的參數來過濾該結果直接設置)

如果該SQL將是靜態我會使用一個函數。在我的情況下,我需要這種動態。 UDF不允許該「EXEC」-stuff和存儲過程的結果集不可重用。

我希望很多人都能像我一樣解決問題。我GOOGLE了很多,並嘗試了幾件事,但沒有什麼作品...

我需要重用存儲過程代碼片段或重用存儲過程的結果集或像存儲過程一樣動態的函數的方法。

那麼有人能幫助我,給我另一個想法嗎?

感謝您的幫助或解決方案的想法:)

V

編輯/解決方案:
所有大家首先應該閱讀 「喬爾Coehoorn」 和 「帕納約蒂斯Kanavos」 的評論。他們是絕對正確的,這個代碼片段不建議在Web應用程序或類似的東西。在我的特殊情況下,它是一個本地應用程序,注入和其他安全方面是不相關的。

之後,我不得不感謝你「阿卜杜勒雷曼賽義德」。基於他的想法,下面的解決方案。

要在SQL中實現該需求,必須創建兩個存儲過程。第一個是創建一個結果集並將其寫入臨時表。

第二個執行第一個存儲過程,篩選結果並刪除臨時表。

第一招:

ALTER PROCEDURE [dbo].[test_SP_FXN](
@database nvarchar(70)= '' 
) AS 

DECLARE @create nvarchar(max)= ' 
    CREATE TABLE ['[email protected]+'].[dbo].[temp_result](
    [name] [nvarchar](150) NULL 
    ,[id] [nvarchar](150) NULL 
    ,[desc] [nvarchar](450) NULL) '; 

DECLARE @sql nvarchar(max)= ' 
INSERT INTO ['[email protected]+'].[dbo].[temp_result] 
    SELECT TOP 1000 name, id, desc 
    FROM ['[email protected]+'].[dbo].[important_table] '; 

EXEC(@create); 
EXEC(@sql); 

第二個:

ALTER PROCEDURE [dbo].[test_SP_2](
@database nvarchar(70)= '' 
) AS 
-- create the temp table with the result of the store procedure 
EXEC ('['[email protected]+'].[dbo].[test_SP_FXN] '[email protected]) 

-- Execute the real sql 
DECLARE @sql nvarchar(max)= 'select * FROM ['[email protected]+'].[dbo].[temp_result] WHERE ID > 5' 
EXEC(@sql); 

-- drop the temp table because there is no use any more 
EXEC ('DROP TABLE ['[email protected]+'].[dbo].[temp_result] ') 

這只是一個例子,但我認爲原則就清楚了。

感謝您的幫助和評論。

+4

這看起來像SQL注入攻擊的噩夢。 –

+0

你是否需要這個過程的結果在臨時表中? –

+0

這不是SQL(語言)的工作原理。您不要「重複使用」多個表格上的語句。一個表等價於一個類型,你不能擁有任意類型的方法。這個存儲過程不是「動態的」,它只是一個使用串聯創建的字符串。事實上,客戶端生成的SQL語句(例如來自ORM)的表現會更好並且更安全。事實上,它會打開安全漏洞(要求訪問不同的數據庫),打開數據庫以注入攻擊,否定執行計劃緩存帶來的任何好處,並可能升級到分佈式事務 –

回答

-1

我認爲你這樣尋找的東西:

Declare @table Table(/* list of columns */) 

insert into @table 
EXEC test_sp 'aDbName','aTable','somecolumns' 

select * 
from @table 
where /* some filters */ 
+0

@Der_V你可以使用Table變量而不是在你的第一個表中創建一個表,並且你不需要刪除它;)。 –

0

以另一種方式,你可以改變你的存儲過程是這樣的:

CREATE PROCEDURE [dbo].[test_sp](
    , @Select nvarchar(70)= '' 
    , @From nvarchar(70)= '' 
    , @Where nvarchar(max) = '' 
    , @GroupByColumn nvarchar(max) = '' 
    , @Having nvarchar(max) = '' 
    , @OrderBy nvarchar(max) = '' 
) 
AS 
    DECLARE @sql nvarchar(max)= 'SELECT ' + @Select + 
           'FROM ' + @From + 
           CASE WHEN @Where = '' THEN '' ELSE ' WHERE ' + @Where END + 
           CASE WHEN @GroupByColumn = '' THEN '' ELSE ' GROUP BY ' + @GroupByColumn END + 
           CASE WHEN @Having = '' THEN '' ELSE ' HAVING ' + @Having END + 
           CASE WHEN @OrderBy = '' THEN '' ELSE ' ORDER BY ' + @OrderBy END 

    EXEC(@sql); 
GO 

現在你可以像這樣測試它:

EXEC test_sp @Select = 'Column1, Column2', @From = 'Table1' 

EXEC test_sp @Select = 'DISTINCT Table1.Column1, Table2.Column1', @FROM = 'Table1 INNER JOIN TABLE2 ON Table1.PK = Table2.FK' 

EXEC test_sp @Select = 't1.[datetime]', @From = '[TestDB].[dbo].[Table1] t1', @Where = 't1.NullableColumn IS NOT NULL' 

EXEC test_sp @Select = 'Column1, Column2', @From = 'Table1', @OrderBy = 'Column2' 

等等......