Quotename 和子字元串的奇怪行為
我看到一些我無法弄清楚的行為。我們有一個儲存過程,它連接一堆表並過濾掉列中的一些管道分隔 (|) 值。我們交叉應用一個字元串拆分器(我相信它非常基於 Jeff Moden 的拆分器)來分隔值。然後我們使用以下內容根據波浪號的位置拉出我們想要查看的部分,將一些文本連接到它,然後將其包裹在括號中。
QUOTENAME( substring(tr.TableVar, 1, charindex('~', tr.TableVar) - 1) +'_someText' )
我們開始看到的問題是錯誤“傳遞給 LEFT 或 SUBSTRING 函式的長度參數無效”。這對我來說通常很明顯:有一個值不包含波浪號。但是,我已經驗證了此查詢將返回的每條記錄實際上都包含一個波浪號。這兩者都沒有進行任何過濾。我用我的眼球驗證了這一點,搜尋具有 0 作為波浪號的 charindex 值的任何值,並蒐索不包含波浪號的任何記錄(不返回任何記錄)。
讓我覺得更奇怪的是,當我刪除 QUOTENAME 函式時,沒有錯誤。我也可以獲取這些值並將它們放在一個臨時表中,對它們執行相同的選擇,它不會產生錯誤。
我還嘗試添加替換以使用插入符號而不是波浪號搜尋(如下所示)…沒有錯誤
QUOTENAME( substring(tr.TableVar, 1, charindex('^', replace(tr.TableVar,'~','^')) - 1) +'_someText' )
在這一點上,我有點抓住稻草。當然,我們可以只使用 replace 方法,但我們也想知道為什麼我們現在遇到這個錯誤(這以前從未發生過)。QUOTENAME 函式中的波浪號是否存在某種 Unicode 字元問題?我什至走在正確的軌道上嗎?
FWIW,我們在 Azure SQL 上並使用排序規則 SQL_Latin1_General_CP1_CI_AS。
僅供參考和範例(實際上是該數據庫中的唯一值)在拆分字元串(無引號)“DOG-1~3~ na ~”之後看起來像這樣。
在此先感謝您的時間。
編輯:我剛剛回去並刪除了連接以檢查沒有連接條件的值。確實存在沒有波浪號的值。我確實知道在某些情況下引擎不會遵循它的邏輯順序(從、哪裡、分組等),因此它可以有效地嘗試在過濾器之前應用子字元串。但是,這讓我想知道為什麼使用 QUOTENAME 會發生這種情況,尤其是為什麼使用替換不會引發錯誤;我的想法是這會導致同樣的問題。一如既往,提前感謝
您懷疑的問題確實是這裡的問題:編譯器可以在任何有意義的時候自由地評估表達式,並且可能比您想像的更早或更晚在計劃中這樣做。這稱為*“延遲表達式評估”*。
即使
Compute Scalar
操作員出現在特定位置時也會出現這種情況,因為它通常只是一個佔位符。您可以判斷,因為它通常在計劃中沒有Actual Rows
值。@PaulWhite 已經在部落格中提到了這一點。我不知道編譯器的內部結構,但我想有一些啟發式方法可以決定何時評估表達式。例如,可能值得儘早評估一個複雜的表達式以避免將大的列值推送到計劃中,或者可能值得推遲它們以避免多次評估它們。
為什麼只有在使用時才會發生這種情況
QUOTENAME
?如果沒有看到計劃或查詢,我絕對不能說,但猜測,無論出於何種原因,編譯器認為呼叫QUOTENAME
SUBSTRING
和CHARINDEX
提早的成本低於延遲它們。**作為參考,**當使用取自or的值呼叫
SUBSTRING
LEFT
or時,始終使用它來避免此類問題。由於它們在未找到搜尋字元串時返回,因此您可以在它到達.RIGHT``CHARINDEX``PATINDEX``NULLIF(...., 0)``0``SUBSTRING
QUOTENAME( substring( tr.TableVar, 1, nullif(charindex('~', tr.TableVar), 0) - 1 ) + '_someText' )