そのため、レポートで使用する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())
しかし、奇妙なのは、データベース内のeverythingであり、tempdbは同じ照合順序を持っています:データベース照合順序はLatin1_General_CI_AS
です(ただし、 UNION ALL
であるLatin1_General_CS_AS
で使用されるテーブル)。
完全に再現可能なサンプルの要旨 、データベースの照合順序がLatin1_General_CI_AS
であることを確認してください。
この問題をどのように正しく解決しますか?これは既知のバグです。これを使い始めたらSQLサーバーがデータを静かに破損することを心配する必要がありますか? 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)
ようやくこれをテストする時間があり、問題を再現することができました。照合の優先順位を順守しない(以下の私の元の回答に記されている)ことはa問題でしたが、-this問題ではありませんでした(両方とも同じ根本的なバグが原因である可能性があります)。これが私が今確認できることです:
NVARCHAR
およびComponent
テーブルのStatement
"Name"列のいずれかの照合順序を、他と異なるものにする必要があります。UNION ALL
の2つの「名前」列間の照合の不一致です。UNION ALL
should haveの照合の不一致により、CONCAT
関数に到達する前にクエリが終了しました。両方の列がVARCHAR
で、少なくとも1つの列がNVARCHAR
である場合、クエリは終了しました。その場合、CONCAT
関数はクエリが正しく終了しないようにします。CONCAT
が関係している場合(少なくともこのUNION ALL
シナリオでは)、クエリが正しく動作しない2つの方法があります。UNION ALL
を含む)がnotである場合、CONCAT
関数の最初のパラメーターであると、誤解を招くエラーメッセージで終了し、エラー「UNION ALL操作」ではなく「concat操作」にあります(エラーメッセージのガベージコレーション名を忘れないでください!)UNION ALL
を含む)isがCONCAT
関数の最初のパラメーターである場合、実際にはsucceedを使用します。 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)で修正されたため、内部で(Microsoftに)通知されているはずですが、SQL ServerでテストしたService Packにはありません。 2012、SP4 GDR(11.0.7462.6)。
この問題を適切に解決する方法
あなたまたは他の誰かがまだSQL Server 2012を使用していて、これが実行されている場合は、UNION ALL
操作内にあるソースでの照合の競合を解決するのが最善です。 notが必要な照合順序を持つ列を含むテーブルを選択し、COLLATE
関数がなくてもUNION ALL
操作がそれ自体で成功するようにCONCAT
句をそこで適用します。使用されています。これは、COLLATE
関数でCONCAT
を指定するよりも優れています。これは、UNION ALL
の後で、この場合shouldは失敗しますが、次のバグにより成功するためです。 CONCAT
。
照合順序の問題(照合順序の問題から生じるエラーメッセージとは別)は、CONCAT
組み込み関数が照合順序の優先順位を尊重しないため、すべての入力パラメーターが同じ照合順序である必要があるためです。明らかに、他と同じ照合順序ではない1つの入力パラメーターがあります。そのパラメーターは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.
*/
また、次の2つのクエリは、照合が各入力パラメーターごとに評価され、最初の入力パラメーターが使用される照合を設定していることを示しています。
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.
*/
この競合は、次の2つの方法で修正できます。
CONCAT
組み込み関数は使用しないでください。 CONCAT
の主な利点は、各パラメーターを文字列型にする必要がないことです。内部で文字列への変換を処理します。これは、連結する文字列以外の項目がいくつかある場合に便利です。しかし、文字列しか持っていない場合は、何のメリットもありません。おそらく、それらすべてを関数に渡すとパフォーマンスが低下します。さらに、CONTACT
を使用しないことで、照合順序の優先順位が引き継がれ、ほとんどの場合、競合が自動的に解決されます。
SELECT 'now this is ' + tmp.Col1
FROM #TT tmp;
-- now this is something
この場合、照合順序の優先順位によって、tmp.Col1
列の照合順序が文字列リテラルの照合順序(「現在の」データベースのデフォルトの照合順序を使用する)をオーバーライドすることが決定されます。
COLLATE
句を使用します(既に行っているように)。これはCOLLATE
キーワードの使用の1つであるため、このアプローチには何の問題もありません。
-- 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
これらの2つのケースでは、使用する照合順序は最初の入力パラメーターによって決定され、この場合、列定義から取得される2番目のパラメーター(下部の例)の照合順序に明示的に設定する必要があります。または、2番目のパラメーターは、最初のパラメーター(上の例)の照合に一致するように明示的に設定する必要があります。この場合、文字列リテラルであるため、データベースのデフォルトから取得されます。
簡単な回避策を探している場合は、 COLLATE DATABASE_DEFAULT を使用すると、物事を再び進めることができます。