SQL Server:無法解決“Latin1_General_CI_AS”和“之間的排序規則衝突”Garbag和G一種rb一種G和garbage'
所以我在 T-SQL 中編寫了一個大型 UDF 用於報告。UDF 包含相當多的公用表表達式。
有一次我正在添加另一個 CTE:
cteCmtCauses AS ( SELECT ProductId = p.Id, Name = hz.Name, CMT = CONCAT(IIF(hz.Cmt_c= '1', 'C', ''), IIF(hz.hz.Cmt_m = '1', 'M', ''), IIF(hz.Cmt_t = '1', 'R', '')) FROM [redacted] UNION ALL SELECT ProductId = p.Id, Name = c.Name, --C = c.Cmr_HasCarcinogenicRisk, --M = c.Cmr_HasMutagenicRisk, --R = c.Cmr_HasToxicForReproductionRisk, CMT = CONCAT(IIF(hz.Cmt_c= '1', 'C', ''), IIF(hz.hz.Cmt_m = '1', 'M', ''), IIF(hz.Cmt_t = '1', 'R', '')) FROM [redacted] ), cteCmtCausesConcat AS ( SELECT ProductId = p.Id, ComponentIds = ( -- Here the issue happens SELECT CONCAT(cte.CMT, N'|', cte.Name, dbo.QueryConcatenationString()) FROM cteCmtCauses cte WHERE cte.ProductId = p.Id FOR XML PATH(N''), TYPE ) FROM [redacted] ),
嘗試保持 UDF 突變時,出現此錯誤:
Msg 468, Level 16, State 9, Procedure QueryProduct, Line 93 Cannot resolve the collation conflict between "Latin1_General_CI_AS" and "널㾍.鉀杫.....祉߾.䊙.鈀杫..." in the concat operation.
事實上,在每次嘗試中,消息都會發生一些變化:
Cannot resolve the collation conflict between "Latin1_General_CI_AS" and "䚋.剀焩.....祉߾.䊙.刀焩..." in the concat operation. Cannot resolve the collation conflict between "Latin1_General_CI_AS" and "꿠䥆.뉀洶.....祉߾.䊙.눀洶..." in the concat operation. Cannot resolve the collation conflict between "Latin1_General_CI_AS" and "꿠洦.퉀洷.....祉߾.䊙.툀洷..." in the concat operation. Cannot resolve the collation conflict between "Latin1_General_CI_AS" and "焐柘.牀䯏.....祉߾.䊙.爀䯏..." in the concat operation.
我能夠通過使用來解決它:
SELECT CONCAT(cte.CMT, N'|', cte.Name COLLATE Latin1_General_CI_AS, dbo.QueryConcatenationString())
但奇怪的是,數據庫和 tempdb 中的所有內容都具有相同的排序規則:數據庫排序規則,存在
Latin1_General_CI_AS
(除了UNION ALL
which is中使用的表之一Latin1_General_CS_AS
)。完整可重現樣本的要點,確保數據庫排序規則
Latin1_General_CI_AS
。如何正確解決此問題,這是一個已知錯誤,我是否需要擔心 SQL Server 在我開始使用此 UDF 後會靜默損壞我的數據?
使用
Microsoft SQL Server 2012 (SP3-GDR) (KB3194721) - 11.0.6248.0 (X64) Sep 23 2016 15:49:43 Copyright (c) Microsoft Corporation Business Intelligence Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1) (Hypervisor)
更新
我終於有時間對此進行測試並能夠重現該問題。雖然不尊重排序規則優先級(在下面我的原始答案中指出)是一個問題,但這不是這個問題(儘管兩者都可能是由相同的潛在錯誤引起的)。這是我現在可以確認的:
OP 的測試腳本(通過 Gist)不會產生此問題,因為它缺少一個關鍵部分:強制排序和表中的
NVARCHAR
“名稱”列之一與另一個不同。Component``Statement
實際錯誤是
UNION ALL
.中的排序規則不匹配
UNION ALL
應該在查詢到達函式之前就結束了查詢CONCAT
,如果兩列都是VARCHAR
,但如果至少有一列是NVARCHAR
,那麼CONCAT
函式會阻止查詢正確終止。
CONCAT
當涉及到查詢時(至少在這種情況下) ,查詢可能會以兩種方式表現不正確UNION ALL
:如果來自子查詢的列(包含排序規則不
UNION ALL
匹配的UNION ALL 操作”(哦,我們不要忘記錯誤消息中的垃圾排序規則名稱!)CONCAT
如果子查詢中的列(包含
UNION ALL
排序規則不匹配的 )是函式的第一個參數CONCAT
,那麼它實際上會成功,對函式返回的值使用數據庫的預設排序規則CONCAT
。(見下面的最終測試案例)
CONCAT
內置函式自 SQL Server 2014 起已修復,因為從該版本開始,這種行為和“原始答案”(見下文)中顯示的不正確行為都無法重現(我在 2014、2016、2017 和2019)。-- DROP TABLE #Mix; CREATE TABLE #Mix ( [VC1] VARCHAR(50) COLLATE SQL_Latin1_General_CP437_CS_AS, [VC2] VARCHAR(50) COLLATE Azeri_Cyrillic_100_CS_AS_WS, [NVC1] NVARCHAR(50) COLLATE Frisian_100_CS_AI_KS, [NVC2] NVARCHAR(50) COLLATE Sami_Sweden_Finland_100_CI_AI ); INSERT INTO #Mix ([VC1], [VC2], [NVC1], [NVC2]) VALUES (0xB0, 0xDE, 0xDE, 0xDE); SELECT * FROM #Mix; /* VC1 VC2 NVC1 NVC2 ░ Ю Þ Þ */ SELECT CONCAT(N'Both VARCHAR', sub.[WhatEva]) FROM ( SELECT [VC1] FROM #Mix UNION ALL SELECT [VC2] FROM #Mix ) sub([WhatEva]); /* Msg 457, Level 16, State 1, Line XXXXX Implicit conversion of varchar value to varchar cannot be performed because the collation of the value is unresolved due to a collation conflict between "Azeri_Cyrillic_100_CS_AS_WS" and "SQL_Latin1_General_CP437_CS_AS" in UNION ALL operator. */ SELECT CONCAT(N'At least one NVARCHAR', sub.[WhatEva]) FROM ( SELECT [VC1] FROM #Mix UNION ALL SELECT [NVC1] FROM #Mix ) sub([WhatEva]); /* -- 2012 Msg 468, Level 16, State 9, Line XXXXX Cannot resolve the collation conflict between "{db_default_collation}" and "堓.ꚤ鍛翹.堓.툀堗.툀堗.쓀姧.꺱䱷..꺱䱷......꺱䱷..툘堗.帎鍲翹.堓..錿翹..." in the concat operation. -- 2014, 2016, 2017, 2019 Msg 451, Level 16, State 1, Line XXXXX Cannot resolve collation conflict between "Frisian_100_CS_AI_KS" and "SQL_Latin1_General_CP437_CS_AS" in UNION ALL operator occurring in SELECT statement column 1. */ -- SUCCESS!?!?!?! Should be an error!!! SELECT CONCAT(sub.[WhatEva], N':At least one NVARCHAR') AS [ConcatSuccessWTF?], SQL_VARIANT_PROPERTY(CONCAT(sub.[WhatEva], N':At least one NVARCHAR'), 'collation') AS [ResultingCollation] FROM ( SELECT [VC1] FROM #Mix UNION ALL SELECT [NVC1] FROM #Mix ) sub([WhatEva]); /* -- 2012 ConcatSuccessWTF? ResultingCollation ░:At least one NVARCHAR {db_default_collation} Þ:At least one NVARCHAR {db_default_collation} -- 2014, 2016, 2017, 2019 Msg 456, Level 16, State 1, Line XXXXX Implicit conversion of nvarchar value to sql_variant cannot be performed because the resulting collation is unresolved due to collation conflict between "Frisian_100_CS_AI_KS" and "SQL_Latin1_General_CP437_CS_AS" in UNION ALL operator. */
這是一個已知的錯誤
不確定它是否在任何公共論壇上被注意到,但它必須在內部(微軟)被注意到,因為它在下一個版本(即 SQL Server 2014)中被修復,儘管在我在 SQL Server 上測試的任何 Service Pack 中都沒有2012,SP4 GDR (11.0.7462.6)。
如何正確解決此問題
如果您或其他任何人仍在使用 SQL Server 2012 並遇到此問題,最好從源頭解決排序規則衝突,即在
UNION ALL
操作中。只需選擇具有您不想要的排序規則的列的表,並在COLLATE
那裡應用子句,以便UNION ALL
操作自行成功,即使沒有CONCAT
使用任何函式。這比COLLATE
在CONCAT
函式中指定 更好,因為這是在 的事實之後UNION ALL
,在這種情況下應該失敗,但由於 中的錯誤而允許成功CONCAT
。原始答案
Collation 問題(與 Collation 問題導致的錯誤消息不同)是由於
CONCAT
內置函式不遵守 Collation Precedence,因此要求所有輸入參數具有相同的 Collation。顯然,您有一個輸入參數與其他參數的排序規則不同。該參數是cte.Name
,您目前已通過COLLATE
關鍵字修復該參數。我們可以模擬這個場景如下。您可以從任何數據庫執行它。我正在執行以下程式碼的數據庫的預設排序規則是:
SQL_Latin1_General_CP1_CI_AS
.CREATE TABLE #TT (Col1 NVARCHAR(50) COLLATE SQL_EBCDIC278_CP1_CS_AS); INSERT INTO #TT values ('something'); SELECT CONCAT('now this is ', tmp.Col1) FROM #TT tmp; /* Msg 468, Level 16, State 9, Line 17 Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "SQL_EBCDIC278_CP1_CS_AS" in the concat operation. */
下面的兩個查詢甚至顯示了根據每個輸入參數評估排序規則,第一個輸入參數設置要使用的排序規則:
SELECT CONCAT('now this is ', tmp.Col1, N' else' COLLATE Latin1_General_100_CI_AS) FROM #TT tmp; /* Msg 468, Level 16, State 9, Line 23 Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "SQL_EBCDIC278_CP1_CS_AS" in the concat operation. Msg 468, Level 16, State 9, Line 23 Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_100_CI_AS" in the concat operation. */ SELECT CONCAT('now this is ' COLLATE Latin1_General_100_CI_AS, tmp.Col1, N' else') FROM #TT tmp; /* Msg 468, Level 16, State 9, Line 30 Cannot resolve the collation conflict between "Latin1_General_100_CI_AS" and "SQL_EBCDIC278_CP1_CS_AS" in the concat operation. Msg 468, Level 16, State 9, Line 30 Cannot resolve the collation conflict between "Latin1_General_100_CI_AS" and "SQL_Latin1_General_CP1_CI_AS" in the concat operation. */
可以通過兩種方式解決此衝突:
- 不要使用
CONCAT
內置函式。的主要好處CONCAT
是它不需要每個參數都是字元串類型;它將在內部處理轉換為字元串。如果您有多個非字元串項要連接,這很方便。但是,如果您只有字元串,那麼它不會提供任何好處,甚至可能會損害將所有這些東西傳遞給函式的性能。並且,如果不使用CONTACT
,排序規則將接管,並且在大多數情況下會自動解決衝突。SELECT 'now this is ' + tmp.Col1 FROM #TT tmp; -- now this is something
在這種情況下,排序規則優先級將確定
tmp.Col1
列的排序規則覆蓋字元串文字的排序規則(它使用“目前”數據庫的預設排序規則)。 2. 使用該COLLATE
子句(正如您已經在做的那樣)。這種方法沒有任何問題,因為這是COLLATE
關鍵字的用途之一。-- Force the Collation of the column in the temp table to match the "current" database: SELECT CONCAT('now this is ', tmp.Col1 COLLATE SQL_Latin1_General_CP1_CI_AS) FROM #TT tmp; -- now this is something -- Force the Collation of the string literal to match the column in the temp table: SELECT CONCAT('now this is ' COLLATE SQL_EBCDIC278_CP1_CS_AS, tmp.Col1) FROM #TT tmp; -- now this is something
在這兩種情況下,要使用的排序規則由第一個輸入參數決定,或者需要明確設置為第二個參數的排序規則(底部範例),在這種情況下,該參數取自列定義。或者,需要顯式設置第二個參數以匹配第一個參數(頂部範例)的排序規則,在這種情況下,它取自數據庫的預設值,因為它是字元串文字。
如果您正在尋找一種快速的解決方法,那麼COLLATE DATABASE_DEFAULT將幫助您重新開始工作。