簡體   English   中英

WHERE 子句中的動態可選參數

[英]Dynamic optional parameters in WHERE clause

考慮 SQL 查詢,所有參數都是可選的

SELECT ...
FROM Table
WHERE
 (@Col1 IS NULL OR Col1 = @Col1)
AND
 (@Col2 IS NULL OR Col2 = @Col2)
...
 (@ColN IS NULL OR ColN = @ColN)

大約有 8 個參數,但將來可能會更多。

代碼審查者要求永遠不要編寫這樣的 WHERE 結構(檢查 NULL 或相等性),而是使用動態 SQL(沒有給出理由)。

對我來說,它看起來可讀且明確。 我還沒有運行執行計划,但理論上 DBMS 會發現任何參數 null 並且不考慮括號內的第二項。 因此,我希望只比較指定的參數。

  1. 有人可以支持或反駁為什么這是不好的嗎?
  2. 索引。 如果搜索參數可以任意組合到達,我們甚至可以索引這樣的查詢(以及如何索引)嗎?

這很復雜。 使用非動態查詢有一些優點:

  • 這是清楚的。
  • 更容易調試和維護。
  • 它在創建存儲過程時進行驗證。
  • SQL 如果代碼在存儲過程中,服務器將為您維護依賴關系。

從編碼的角度來看,我認為編譯時驗證是一個巨大的勝利。 它可以防止意外的運行時故障。

動態查詢在這種情況下有一個優勢:它可能會在每次執行時重新編譯。

這什么時候會有所作為? 如果您在每個使用的列上都有一個索引,那么重新編譯總是有幫助的。

但是,您可以使用option (recompile)兩全其美。 這將根據當前參數值重新編譯代碼。 這可能是您想要做的事情的最佳選擇。

我傾向於同意你的審稿人。 原因如下:

你所擁有的風險是 SQL 服務器將在第一次執行時編譯此查詢一次 它將根據當時提供的參數值進行基數估計,並以此為基礎制定執行計划。 該執行計划將在緩存中保留一段時間,而不管后續運行中實際發生了什么。 這可能導致某些參數值的執行計划非常糟糕。

正如 Linoff 先生所建議的(他真的很了解他的東西),您可以使用option(recompile)來解釋這一點。 但是,這意味着 SQL 服務器將需要在每次執行時重新編譯此查詢。 如果經常運行這些查詢,可能會導致 SQL 服務器隨着時間的推移花費大量額外的精力重新編譯。

此外,我對此類查詢的經驗是,用戶傾向於比其他列更頻繁地提供某些列。 如果您動態構建查詢,SQL 服務器可以緩存常見排列的計划,並節省額外的重新編譯工作。 從 DBA 的角度來看,這些現在也是計划緩存中的單獨條目,您可以檢查這些條目以了解隨着時間的推移哪些索引可能更有價值以支持此搜索功能。

當然,要知道這是否會成功,就意味着要了解您的數據、系統和用戶。 您必須將工作返回到 go 並檢查用戶是否真的在同一組列上進行搜索,或者它是否更隨機分布。

我也可能做不同的事情是我傾向於在客戶端代碼中構建動態 SQL,這聽起來有點像您希望將其作為存儲過程的一部分來執行。

例如,使用類似 C# 的偽代碼:

sql = "SELECT ... FROM... WHERE 1=1";

if (txtCol1.Text != "")
{
   sql += " AND Col1 = @Col1"
   cmd.Parameters.AddWithValue("@Col1", txtCol1.Text);
}
if (txtCol1.Text != "")
{
    sql += " AND Col2 = @Col2"
    cmd.Parameters.AddWithValue("@Col2", txtCol2.Text);
}
//...
// Note this is just pseudocode. I'm not a fan of AddWithValue() in actual practice.

cmd.ExecuteReader();

我知道1=1這件事看起來很奇怪,但它不會傷害 SQL 服務器,這是一種確保語法仍然有效的簡單方法,無論哪個(如果有)條件首先持有一個值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM