2009-01-22 64 views
2

DBA在這裏工作是試圖把我直截了當的存儲過程變成一個動態的SQL怪物。無可否認,我的存儲過程可能不如他們想要的那麼快,但我不禁相信有足夠的方式來做基本上是有條件的連接。有條件的連接 - 動態SQL

這裏是我的存儲過程的一個例子:

SELECT 
* 
FROM 
table 
WHERE 
(
    @Filter IS NULL OR table.FilterField IN 
    (SELECT Value FROM dbo.udfGetTableFromStringList(@Filter, ',')) 
) 

的UDF把一個逗號分隔的過濾器列表(例如,銀行名稱)到表。

很明顯,在where子句中使用過濾條件並不理想。歡迎任何關於基於存儲的proc參數有條件加入的更好方式的建議。除此之外,有沒有人對動態sql方法有任何建議?

感謝

+0

「這裏的DBA正努力將我直截了當的存儲過程轉變爲動態的sql怪物。」 - 這很有趣,它通常是我的另一種方式;) – RedFilter 2009-01-22 15:28:42

回答

4

你可以INNER上表連接從UDF返回而不是IN子句

你的UDF可能是這樣的

在使用它
CREATE FUNCTION [dbo].[csl_to_table] (@list varchar(8000)) 
RETURNS @list_table TABLE ([id] INT) 
AS 
BEGIN 
    DECLARE  @index INT, 
      @start_index INT, 
      @id INT 

    SELECT @index = 1 
    SELECT @start_index = 1 
    WHILE @index <= DATALENGTH(@list) 
    BEGIN 

     IF SUBSTRING(@list,@index,1) = ',' 
     BEGIN 

      SELECT @id = CAST(SUBSTRING(@list, @start_index, @index - @start_index) AS INT) 
      INSERT @list_table ([id]) VALUES (@id) 
      SELECT @start_index = @index + 1 
     END 
     SELECT @index = @index + 1 
    END 
    SELECT @id = CAST(SUBSTRING(@list, @start_index, @index - @start_index) AS INT) 
    INSERT @list_table ([id]) VALUES (@id) 
    RETURN 
END 

然後在返回的表中的ID上INNER JOIN。這UDF假定你傳遞在你的逗號分開的INT

編輯:

爲了處理空或沒有值被傳遞在@filter,最直接的方法,我可以請參閱將基於@filter值在sproc中執行不同的查詢。我不確定這會如何影響緩存的執行計劃(如果有人可以確認,會更新),或者最終結果會比原始的sproc更快,我認爲這裏的答案就在於測試。

+0

處收縮他,但是如果@Filter爲空並沒有通過?這不會導致存儲過程不返回任何數據。 – 2009-01-22 14:55:45

2

看起來好像在另一個答案中重寫了代碼,但是在存儲過程中針對動態SQL的一個很好的論據是它打破了所有權鏈。

也就是說,當你正常調用存儲過程時,它在執行動態SQL和execute命令時,在存儲過程所有者的權限EXCEPT下執行,對於動態SQL的上下文,它將恢復到權限調用者,這可能是不受歡迎的取決於您的安全模型。最後,爲了解決DBA的問題並避免動態SQL,最好將其妥善解決並重寫。

+0

謝謝約翰。這是很好的信息。任何想法如何實現這一目標? 根據Erland在http://www.sommarskog.se/dyn-search-2005.html他似乎建議動態sql的確是要走的路,但在http://www.sommarskog.se/dynamic_sql。 html#他建議的名單否則。 謝謝。 – 2009-01-22 15:14:40

2

我不知道我理解你對動態SQL的厭惡。也許這是因爲你的UDF很好地抽象出了一些問題的混亂,並且你感覺到動態SQL會帶來這些後果。那麼,考慮到大部分(如果不是全部的話)DAL或ORM工具將廣泛地依賴於動態SQL,我認爲你的問題可以重申爲「我怎樣才能很好地抽象出動態SQL的混亂性」。

就我而言,動態SQL給了我完全我想要的查詢,以及隨後的性能和行爲,我正在尋找。

+0

當你使用動態sql時,你會失去所有的「編譯時」檢查。一切都變成了「運行時間」錯誤。這是我的大問題。即使DBA「新增和改進」了動態SQL存儲過程,它也會產生「運行時」錯誤。 – 2009-01-22 16:12:39

+0

動態SQl也不太安全。它要求您在表級別設置權限,而不是在存儲的proc級別設置權限。因此,用戶可以直接訪問表格,而不是僅限於處理proc中的內容。這導致欺詐很容易犯的情況。 – HLGEM 2009-01-22 16:23:12

2

我沒有看到你的方法有什麼問題。誠實地說,將它重寫爲使用動態SQL來基於@Filter是否爲null來執行兩個不同的查詢似乎很愚蠢。

我能看到的唯一潛在缺點是它可能會在確定一個好的執行計劃時造成一些困難。但如果表現足夠好,就沒有理由去改變它。

1

不管你做什麼(這裏的答案都有好處),一定要比較每個選項的性能和執行計劃。

有時,如果手優化影響您的代碼可維護性,並且確實在代碼執行方式上沒有差異,那麼手優化就毫無意義。

我先簡單看一下與NULL檢查改變IN一個簡單LEFT JOIN(這並不能擺脫你的UDF的,但它應該只被調用一次):

SELECT * 
FROM table 
LEFT JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter 
    ON table.FilterField = filter.Value 
WHERE @Filter IS NULL 
    OR filter.Value IS NOT NULL 
1

看來,你試圖寫AA單查詢處理兩種情形:
1. @filter = 「X,Y,Z」
2. @filter IS NULL

要優化方案2,我會INNER JOIN上UDF,而不是使用IN子句...

SELECT * FROM table 
INNER JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter 
    ON table.FilterField = filter.Value 

要優化方案2,我不會試圖去適應現有的查詢,而不是我刻意保留的情況下分開,要麼一個IF語句或UNION和一個WHERE子句模擬IF ...

TSQL IF

IF (@filter IS NULL) 
    SELECT * FROM table 
ELSE 
    SELECT * FROM table 
    INNER JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter 
    ON table.FilterField = filter.Value 


聯盟模擬IF

SELECT * FROM table 
    INNER JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter 
    ON table.FilterField = filter.Value 

UNION ALL 

SELECT * FROM table WHERE @filter IS NULL 

這種設計的優點是每種情況都很簡單,而確定哪一種很簡單就是很簡單。然而,將這兩者合併成一個查詢會導致諸如LEFT JOIN等妥協,因此會給每個查詢帶來顯着的性能損失。