2005年から2012年までのSQL Serverのさまざまなバージョンにわたって、sys.databases
に含まれるさまざまな列でUNPIVOT
を実行しようとしています。
UNPIVOT
が失敗し、次のエラーメッセージが表示されます。
メッセージ8167、レベル16、状態1、行48
列「CompatibilityLevel」のタイプが、UNPIVOTリストで指定されている他の列のタイプと競合しています。
T-SQL:
DECLARE @dbname SYSNAME;
SET @dbname = DB_NAME();
SELECT [Database] = unpvt.DatabaseName
, [Configuration Item] = unpvt.OptionName
, [Configuration Value] = unpvt.OptionValue
FROM (
SELECT
DatabaseName = name
, RecoveryModel = CONVERT(VARCHAR(50), d.recovery_model_desc)
, CompatibilityLevel = CONVERT(VARCHAR(50), CASE d.[compatibility_level] WHEN 70 THEN 'SQL Server 7' WHEN 80 THEN 'SQL Server 2000' WHEN 90 THEN 'SQL Server 2005' WHEN 100 THEN 'SQL Server 2008' WHEN 110 THEN 'SQL Server 2012' WHEN 120 THEN 'SQL Server 2014' ELSE 'UNKNOWN' END)
, AutoClose = CONVERT(VARCHAR(50), CASE d.is_auto_close_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoCreateStatistics = CONVERT(VARCHAR(50), CASE d.is_auto_create_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoShrink = CONVERT(VARCHAR(50), CASE d.is_auto_shrink_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoUpdateStatistics = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoUpdateStatisticsAsynch = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_async_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, CloseCursorOnCommit = CONVERT(VARCHAR(50), CASE d.is_cursor_close_on_commit_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DefaultCursor = CONVERT(VARCHAR(50), CASE d.is_local_cursor_default WHEN 1 THEN 'LOCAL' ELSE 'GLOBAL' END)
, ANSINULL_Default = CONVERT(VARCHAR(50), CASE d.is_ansi_null_default_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSINULLS_Enabled = CONVERT(VARCHAR(50), CASE d.is_ansi_nulls_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSIPadding_Enabled = CONVERT(VARCHAR(50), CASE d.is_ansi_padding_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSIWarnings_Enabled = CONVERT(VARCHAR(50), CASE d.is_ansi_warnings_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ArithmeticAbort_Enabled = CONVERT(VARCHAR(50), CASE d.is_arithabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ConcatNullYieldsNull = CONVERT(VARCHAR(50), CASE d.is_concat_null_yields_null_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, CrossDBOwnerChain = CONVERT(VARCHAR(50), CASE d.is_db_chaining_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DateCorrelationOptimized = CONVERT(VARCHAR(50), CASE d.is_date_correlation_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, NumericRoundAbort = CONVERT(VARCHAR(50), CASE d.is_numeric_roundabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, [Parameterization] = CONVERT(VARCHAR(50), CASE d.is_parameterization_forced WHEN 0 THEN 'SIMPLE' ELSE 'FORCED' END)
, QuotedIdentifiers_Enabled = CONVERT(VARCHAR(50), CASE d.is_quoted_identifier_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, RecursiveTriggers_Enabled = CONVERT(VARCHAR(50), CASE d.is_recursive_triggers_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, [TrustWorthy] = CONVERT(VARCHAR(50), CASE d.is_trustworthy_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, VARDECIMAL_Storage = CONVERT(VARCHAR(50), 'TRUE')
, PageVerify = CONVERT(VARCHAR(50), page_verify_option_desc )
, BrokerEnabled = CONVERT(VARCHAR(50), CASE d.is_broker_enabled WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DatabaseReadOnly = CONVERT(VARCHAR(50), CASE d.is_read_only WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, EncryptionEnabled = CONVERT(VARCHAR(50), CASE d.is_encrypted WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, RestrictedAccess = CONVERT(VARCHAR(50), user_access_desc)
, Collation = CONVERT(VARCHAR(50), d.collation_name)
FROM sys.databases d
WHERE name = @dbname
OR @dbname IS NULL
) src
UNPIVOT
(
OptionValue FOR OptionName IN
(
RecoveryModel
, CompatibilityLevel
, AutoClose
, AutoCreateStatistics
, AutoShrink
, AutoUpdateStatistics
, AutoUpdateStatisticsAsynch
, CloseCursorOnCommit
, DefaultCursor
, ANSINULL_Default
, ANSINULLS_Enabled
, ANSIPadding_Enabled
, ANSIWarnings_Enabled
, ArithmeticAbort_Enabled
, ConcatNullYieldsNull
, CrossDBOwnerChain
, DateCorrelationOptimized
, NumericRoundAbort
, [Parameterization]
, QuotedIdentifiers_Enabled
, RecursiveTriggers_Enabled
, [TrustWorthy]
, VARDECIMAL_Storage
, PageVerify
, BrokerEnabled
, DatabaseReadOnly
, EncryptionEnabled
, RestrictedAccess
, Collation
)
) AS unpvt;
これは、次のような、指定されたデータベースのデータベースオプションのリストを適切にフォーマットするように設計されています。
+----------+----------------------------+----------------------------+
| Database | Configuration Item | Value in Use |
+----------+----------------------------+----------------------------+
| master | RecoveryModel | SIMPLE |
| master | CompatibilityLevel | SQL Server 2008 |
| master | AutoClose | FALSE |
| master | AutoCreateStatistics | TRUE |
| master | AutoShrink | FALSE |
| master | AutoUpdateStatistics | TRUE |
| master | AutoUpdateStatisticsAsynch | FALSE |
| master | CloseCursorOnCommit | FALSE |
| master | DefaultCursor | GLOBAL |
| master | ANSINULL_Default | FALSE |
| master | ANSINULLS_Enabled | FALSE |
| master | ANSIPadding_Enabled | FALSE |
| master | ANSIWarnings_Enabled | FALSE |
| master | ArithmeticAbort_Enabled | FALSE |
| master | ConcatNullYieldsNull | FALSE |
| master | CrossDBOwnerChain | TRUE |
| master | DateCorrelationOptimized | FALSE |
| master | NumericRoundAbort | FALSE |
| master | Parameterization | SIMPLE |
| master | QuotedIdentifiers_Enabled | FALSE |
| master | RecursiveTriggers_Enabled | FALSE |
| master | TrustWorthy | TRUE |
| master | VARDECIMAL_Storage | TRUE |
| master | PageVerify | CHECKSUM |
| master | BrokerEnabled | FALSE |
| master | DatabaseReadOnly | FALSE |
| master | EncryptionEnabled | FALSE |
| master | RestrictedAccess | MULTI_USER |
| master | Collation | Latin1_General_CI_AS_KS_WS |
+----------+----------------------------+----------------------------+
Latin1_General_CI_AS_KS_WS
照合を使用してサーバーでこれを実行すると、ステートメントは成功します。特定のフィールドにCOLLATE
句が含まれるようにT-SQLを変更すると、他の照合順序を持つサーバーで実行されます。
Latin1_General_CI_AS_KS_WS
以外の照合順序を持つサーバーで機能するコードは次のとおりです。
DECLARE @dbname SYSNAME;
SET @dbname = DB_NAME();
SELECT [Database] = unpvt.DatabaseName
, [Configuration Item] = unpvt.OptionName
, [Configuration Value] = unpvt.OptionValue
FROM (
SELECT
DatabaseName = name
, RecoveryModel = CONVERT(VARCHAR(50), d.recovery_model_desc) COLLATE SQL_Latin1_General_CP1_CI_AS
, CompatibilityLevel = CONVERT(VARCHAR(50), CASE d.[compatibility_level] WHEN 70 THEN 'SQL Server 7' WHEN 80 THEN 'SQL Server 2000' WHEN 90 THEN 'SQL Server 2005' WHEN 100 THEN 'SQL Server 2008' WHEN 110 THEN 'SQL Server 2012' WHEN 120 THEN 'SQL Server 2014' ELSE 'UNKNOWN' END)
, AutoClose = CONVERT(VARCHAR(50), CASE d.is_auto_close_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoCreateStatistics = CONVERT(VARCHAR(50), CASE d.is_auto_create_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoShrink = CONVERT(VARCHAR(50), CASE d.is_auto_shrink_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoUpdateStatistics = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoUpdateStatisticsAsynch = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_async_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, CloseCursorOnCommit = CONVERT(VARCHAR(50), CASE d.is_cursor_close_on_commit_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DefaultCursor = CONVERT(VARCHAR(50), CASE d.is_local_cursor_default WHEN 1 THEN 'LOCAL' ELSE 'GLOBAL' END)
, ANSINULL_Default = CONVERT(VARCHAR(50), CASE d.is_ansi_null_default_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSINULLS_Enabled = CONVERT(VARCHAR(50), CASE d.is_ansi_nulls_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSIPadding_Enabled = CONVERT(VARCHAR(50), CASE d.is_ansi_padding_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSIWarnings_Enabled = CONVERT(VARCHAR(50), CASE d.is_ansi_warnings_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ArithmeticAbort_Enabled = CONVERT(VARCHAR(50), CASE d.is_arithabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ConcatNullYieldsNull = CONVERT(VARCHAR(50), CASE d.is_concat_null_yields_null_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, CrossDBOwnerChain = CONVERT(VARCHAR(50), CASE d.is_db_chaining_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DateCorrelationOptimized = CONVERT(VARCHAR(50), CASE d.is_date_correlation_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, NumericRoundAbort = CONVERT(VARCHAR(50), CASE d.is_numeric_roundabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, [Parameterization] = CONVERT(VARCHAR(50), CASE d.is_parameterization_forced WHEN 0 THEN 'SIMPLE' ELSE 'FORCED' END)
, QuotedIdentifiers_Enabled = CONVERT(VARCHAR(50), CASE d.is_quoted_identifier_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, RecursiveTriggers_Enabled = CONVERT(VARCHAR(50), CASE d.is_recursive_triggers_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, [TrustWorthy] = CONVERT(VARCHAR(50), CASE d.is_trustworthy_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, VARDECIMAL_Storage = CONVERT(VARCHAR(50), 'TRUE')
, PageVerify = CONVERT(VARCHAR(50), page_verify_option_desc ) COLLATE SQL_Latin1_General_CP1_CI_AS
, BrokerEnabled = CONVERT(VARCHAR(50), CASE d.is_broker_enabled WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DatabaseReadOnly = CONVERT(VARCHAR(50), CASE d.is_read_only WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, EncryptionEnabled = CONVERT(VARCHAR(50), CASE d.is_encrypted WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, RestrictedAccess = CONVERT(VARCHAR(50), user_access_desc) COLLATE SQL_Latin1_General_CP1_CI_AS
, Collation = CONVERT(VARCHAR(50), d.collation_name)
FROM sys.databases d
WHERE name = @dbname
OR @dbname IS NULL
) src
UNPIVOT
(
OptionValue FOR OptionName IN
(
RecoveryModel
, CompatibilityLevel
, AutoClose
, AutoCreateStatistics
, AutoShrink
, AutoUpdateStatistics
, AutoUpdateStatisticsAsynch
, CloseCursorOnCommit
, DefaultCursor
, ANSINULL_Default
, ANSINULLS_Enabled
, ANSIPadding_Enabled
, ANSIWarnings_Enabled
, ArithmeticAbort_Enabled
, ConcatNullYieldsNull
, CrossDBOwnerChain
, DateCorrelationOptimized
, NumericRoundAbort
, [Parameterization]
, QuotedIdentifiers_Enabled
, RecursiveTriggers_Enabled
, [TrustWorthy]
, VARDECIMAL_Storage
, PageVerify
, BrokerEnabled
, DatabaseReadOnly
, EncryptionEnabled
, RestrictedAccess
, Collation
)
) AS unpvt;
観察された動作は、以下のフィールドはサーバーの照合もデータベースの照合も観察しないことです。それらはalwaysLatin1_General_CI_AS_KS_WS
照合で提示されます。
SQL Server 2012では、sys.sp_describe_first_result_set
を使用して、特定のクエリから返された列に関するメタデータを簡単に取得できます。以下を使用して、照合の不一致を判別しました。
DECLARE @cmd NVARCHAR(MAX);
SET @cmd = '
SELECT
DatabaseName = CONVERT(VARCHAR(50), d.name)
, RecoveryModel = CONVERT(VARCHAR(50), d.recovery_model_desc)
, Collation = CONVERT(VARCHAR(50), d.collation_name)
FROM sys.databases d
WHERE name = DB_NAME();
';
EXEC sp_describe_first_result_set @command = @cmd;
結果:
これらの列の照合が静的に設定されるのはなぜですか?
マイクロソフトからの公式の言葉:
事前定義された文字列(タイプ、システムの説明、定数など)を含む一部の列は、常に特定の照合順序
Latin1_General_CI_AS_KS_WS
に固定されています。これは、インスタンス/データベースの照合とは無関係です。その理由は、これはシステムメタデータ(ユーザーメタデータではない)であり、基本的にこれらの文字列は大文字と小文字を区別せずに処理されるためです(キーワードのように常にラテン語です)。
オブジェクト名、列名、インデックス名、ログイン名などのユーザーメタデータを含むシステムテーブルの他の列は、インスタンスまたはデータベースの照合順序をとります。インスタンス照合の場合はSQL Serverのインストール時、およびデータベース照合の場合はデータベースの作成時に、列は適切な照合に照合されます。
あなたは尋ねました(私の強調):
なぜこれらの列の照合は静的に設定されていますか?
一部の列が静的に設定される理由は、クエリがサーバーまたはデータベースの照合(さらに重要なこと:CaSe SenSiTIviTy)が正しく機能することを心配する必要がないようにするためです。このクエリは、照合に関係なく常に機能します。
SELECT * FROM sys.databases WHERE state_desc = N'ONLine';
一方、サーバーの照合で大文字と小文字が区別される場合、上記のクエリは次のように0行を返します。
SELECT * FROM sys.databases
WHERE state_desc COLLATE Albanian_BIN = N'ONLine';
たとえば、SQL_Estonian_CP1257_CS_AS
照合を使用してSQL Serverのインスタンスをインストールする場合は、次のコマンドを実行します。
SELECT name, collation_name
FROM master.sys.all_columns
WHERE collation_name IS NOT NULL
AND [object_id] = OBJECT_ID(N'sys.databases');
次の結果が表示されます(SQL Serverのバージョンによっては、同様の結果になります)。
name SQL_Estonian_CP1257_CS_AS
collation_name SQL_Estonian_CP1257_CS_AS
user_access_desc Latin1_General_CI_AS_KS_WS
state_desc Latin1_General_CI_AS_KS_WS
snapshot_isolation_state_desc Latin1_General_CI_AS_KS_WS
recovery_model_desc Latin1_General_CI_AS_KS_WS
page_verify_option_desc Latin1_General_CI_AS_KS_WS
log_reuse_wait_desc Latin1_General_CI_AS_KS_WS
default_language_name SQL_Estonian_CP1257_CS_AS
default_fulltext_language_name SQL_Estonian_CP1257_CS_AS
containment_desc Latin1_General_CI_AS_KS_WS
delayed_durability_desc SQL_Estonian_CP1257_CS_AS
次に、masterデータベースからサーバーの照合順序を継承するのではなく、データベースの照合順序を継承するメタデータビューを示します。
CREATE DATABASE server_collation;
GO
CREATE DATABASE albanian COLLATE Albanian_BIN;
GO
CREATE DATABASE hungarian COLLATE Hungarian_Technical_100_CS_AI;
GO
SELECT name, collation_name
FROM server_collation.sys.all_columns
WHERE collation_name IS NOT NULL
AND object_id = -391; -- sys.columns
SELECT name, collation_name
FROM albanian.sys.all_columns
WHERE collation_name IS NOT NULL
AND object_id = -391; -- sys.columns
SELECT name, collation_name
FROM hungarian.sys.all_columns
WHERE collation_name IS NOT NULL
AND object_id = -391; -- sys.columns
結果:
server_collation
----------------
name SQL_Estonian_CP1257_CS_AS
collation_name SQL_Estonian_CP1257_CS_AS
generated_always_type_desc Latin1_General_CI_AS_KS_WS
encryption_type_desc Latin1_General_CI_AS_KS_WS
encryption_algorithm_name Latin1_General_CI_AS_KS_WS
column_encryption_key_database_name SQL_Estonian_CP1257_CS_AS
albanian
----------------
name Albanian_BIN
collation_name Albanian_BIN
generated_always_type_desc Latin1_General_CI_AS_KS_WS
encryption_type_desc Latin1_General_CI_AS_KS_WS
encryption_algorithm_name Latin1_General_CI_AS_KS_WS
column_encryption_key_database_name Albanian_BIN
hungarian
----------------
name Hungarian_Technical_100_CS_AI
collation_name Hungarian_Technical_100_CS_AI
generated_always_type_desc Latin1_General_CI_AS_KS_WS
encryption_type_desc Latin1_General_CI_AS_KS_WS
encryption_algorithm_name Latin1_General_CI_AS_KS_WS
column_encryption_key_database_name Hungarian_Technical_100_CS_AI
したがって、この場合、いくつかの列はデータベース照合を継承し、他の列はこの「汎用」Latin1照合に固定されていることがわかります。つまり、特定の名前とプロパティを上記の大文字と小文字の区別の問題から隔離するために使用されます。
たとえば、UNION
を実行しようとすると、次のようになります。
SELECT name FROM albanian.sys.columns
UNION ALL
SELECT name FROM server_collation.sys.columns;
あなたはこのエラーを受け取ります:
メッセージ451、レベル16、状態1
SELECTステートメントの列1で発生するUNION ALL演算子の「Albanian_BIN」と「SQL_Estonian_CP1257_CS_AS」の間の照合の競合を解決できません.
同様に、PIVOT
またはUNPIVOT
を実行しようとすると、ルールはさらに厳しくなります(出力タイプはすべて一致する必要があります正確に単に互換性があるのではなく)、しかしエラーメッセージはあまり役に立たず、誤解を招くほどです:
メッセージ8167、レベル16、状態1
列「列名」のタイプが、UNPIVOTリストで指定されている他の列のタイプと競合しています。
クエリで明示的なCOLLATE
句を使用して、これらのエラーを回避する必要があります。たとえば、上記の共用体は次のようになります。
SELECT name COLLATE Latin1_General_CI_AS_KS_WS
FROM albanian.sys.columns
UNION ALL
SELECT name COLLATE Latin1_General_CI_AS_KS_WS
FROM server_collation.sys.columns;
これが問題を引き起こす可能性があるのは、照合が強制されているが同じ文字表現が含まれていない場合、またはソートが使用され、強制された照合でソースとは異なるソート順が使用されている場合に、出力が混乱する場合のみです。
システムカタログビューのさまざまなフィールドの照合順序に関して表示される動作は、各フィールドの定義方法と照合順序の結果です。
_sys.databases
_を見るとき、それがテーブルではないことを覚えておくことが重要です。以前は(SQL Server 2000で終わると思います)これらはシステムカタログtablesでしたが、現在はシステムカタログviewsになっています。したがって、それらの情報のソースは、必ずしも現在のデータベースコンテキスト(または_master.sys.databases
_などの完全修飾オブジェクトを処理するときの指定されたデータベースのコンテキスト)からのものであるとは限りません。
特に_sys.databases
_を扱うと、一部のフィールドは_[master]
_データベース(インスタンスのデフォルトの照合に基づく照合で作成されたもの-サーバーレベルの照合)から取得され、一部のフィールドは式(つまりCASE
ステートメント)、および一部は「非表示」ソース、つまり_[mssqlsystemresource]
_データベースからのものです。また、_[mssqlsystemresource]
_データベースには、_Latin1_General_CI_AS_KS_WS
_という照合順序があります。
name
フィールドは、_master.sys.sysdbreg
_のname
フィールドから供給されます。したがって、このフィールドは常に_[master]
_データベースの照合にある必要があり、これもサーバーの照合に一致します。
ただし、_sys.databases
_の以下のフィールドは、_[name]
_の_[mssqlsystemresource].[sys].[syspalvalues]
_フィールドから取得されます。
これらのフィールドには常に_Latin1_General_CI_AS_KS_WS
_の照合順序が必要です。
ただし、_collation_name
_フィールドは次の式から取得されます。
_CONVERT(nvarchar(128),
CASE
WHEN serverproperty('EngineEdition')=5
AND [master].[sys].[sysdbreg].[id] as [d].[id]=(1)
THEN serverproperty('collation')
ELSE collationpropertyfromid(
CONVERT(int,
isnull([master].[sys].[sysobjvalues].[value] as [coll].[value],
CONVERT_IMPLICIT(sql_variant,DBPROP.[cid],0)
),
0),'name')
END,
0)
_
これが Collation Precedence の登場です。ここでの出力の両方のオプションは、システム関数です:serverproperty()
およびcollationpropertyfromid()
この式の照合は、「強制可能なデフォルト」と見なされます。
Transact-SQL文字列変数、パラメーター、リテラル、またはカタログ組み込み関数の出力、または文字列入力をとらずに文字列出力を生成する組み込み関数。
オブジェクトがユーザー定義関数、ストアドプロシージャ、またはトリガーで宣言されている場合、オブジェクトには、関数、ストアドプロシージャ、またはトリガーが作成されたデータベースの既定の照合順序が割り当てられます。オブジェクトがバッチで宣言されている場合、オブジェクトには、接続用の現在のデータベースのデフォルトの照合が割り当てられます。
その2番目の段落に照らして、_sys.databases
_はmaster
データベースに存在するビューであるため、master
データベース(現在のデータベースではない)の照合順序になります。
_state_desc
_フィールドも式です。
_CASE
WHEN serverproperty('EngineEdition')=5
AND [Expr1081]=(1)
THEN N'RESTORING'
ELSE
CASE
WHEN serverproperty('EngineEdition')=5
AND CONVERT(bit,
[master].[sys].[sysdbreg].[status] as [d].[status]&(128),
0)=(1)
THEN N'COPYING'
ELSE
CASE
WHEN serverproperty('EngineEdition')=5
AND CONVERT(bit,
[master].[sys].[sysdbreg].[status] as [d].[status]&(256),
0)=(1)
THEN N'SUSPECT'
ELSE [mssqlsystemresource].[sys].[syspalvalues].[name] as [st].[name]
END
END
END
_
ただし、この式の照合は_Latin1_General_CI_AS_KS_WS
_です。どうして?さて、この式には新しいものが導入されています。実際のフィールドへの参照:最後のELSE
句の_[mssqlsystemresource].[sys].[syspalvalues].[name]
_列参照は「暗黙的」と見なされます。
列参照。式の照合は、テーブルまたはビューの列に定義されている照合から取得されます。
もちろん、これは興味深い質問を開きます。この式は、CASE
の評価方法に応じて異なる照合を返すことができますか?リテラルは、このオブジェクトが定義されているデータベースの照合にありますが、ELSE
条件は、元の照合を保持する必要があるフィールド値を返します。さいわい、 sys.dm_exec_describe_first_result_set 動的管理関数を使用してテストをシミュレートできます。
_-- Force ELSE condition
SELECT system_type_name, max_length, collation_name
FROM sys.dm_exec_describe_first_result_set(N'
DECLARE @A INT;
SET @A = -1;
SELECT CASE WHEN @A = 100 THEN N''All About the Benjamins''
ELSE [name]
END AS [Stuff]
FROM msdb.dbo.sysjobs
', NULL, NULL) rs
-- Force WHEN condition
SELECT system_type_name, max_length, collation_name
FROM sys.dm_exec_describe_first_result_set(N'
DECLARE @A INT;
SET @A = 100;
SELECT CASE WHEN @A = 100 THEN N''All About the Benjamins''
ELSE [name]
END AS [Stuff]
FROM msdb.dbo.sysjobs
', NULL, NULL) rs
-- Control test
SELECT system_type_name, max_length, collation_name
FROM sys.dm_exec_describe_first_result_set(N'
DECLARE @A INT;
SET @A = 100;
SELECT CASE WHEN @A = 100 THEN N''All About the Benjamins''
ELSE N''Whazzup, yo?!?!?''
END AS [Stuff]
', NULL, NULL) rs
_
戻り値(照合順序が_SQL_Latin1_General_CP1_CI_AS
_で設定されているが、照合順序が_Japanese_Unicode_CI_AS
_のデータベースで実行されているインスタンス):
_system_type_name max_length collation_name
---------------- ---------- --------------
nvarchar(128) 256 SQL_Latin1_General_CP1_CI_AS
nvarchar(128) 256 SQL_Latin1_General_CP1_CI_AS
nvarchar(23) 46 Japanese_Unicode_CI_AS
_
ここでは、_[msdb]
_のフィールドを参照する2つのクエリが_[msdb]
_データベース(システムDBであり、サーバーの照合順序によって決定された)の照合順序を取得していることがわかります。
観察された動作は、以下のフィールドはサーバーの照合もデータベースの照合も観察しないことです。それらはalways _
Latin1_General_CI_AS_KS_WS
_照合で提示されます。
サーバーの照合またはデータベースの照合に関係なく、これらのフィールドの照合は常に_Latin1_General_CI_AS_KS_WS
_の照合になります。そしてその理由は、照合の優先順位です。これらのフィールドは_[mssqlsystemresource]
_データベースのテーブルから取得され、優先度が最も高いため、明示的なCOLLATE
句でオーバーライドされない限り、最初の照合を保持します。
Explicit =式でCOLLATE句を使用して特定の照合に明示的にキャストされる式。
明示は暗黙よりも優先されます。暗黙はCoercible-defaultに優先します。
明示的>暗黙的>強制デフォルト
そして関連する質問:
これらの列の照合が静的に設定されるのはなぜですか?
それらが静的に設定されているということでも、他のフィールドが何らかの形で動的であるということでもありません。これらのすべてのシステムカタログビューのすべてのフィールドは、照合順序の同じルールで動作します。それらが他のフィールドよりも「静的」であるように見える理由(つまり、SQL Serverを別のデフォルト照合でインストールしても変更されないため、デフォルト照合でシステムデータベースが作成される)は、_[mssqlsystemresource]
_データベースは、SQL Serverのすべてのインストールで一貫して_Latin1_General_CI_AS_KS_WS
_の照合順序を持っています(または確実に表示されます)。 SQL Server自体を内部で管理するのが難しいため(つまり、内部ロジックに使用される並べ替えおよび比較ルールがインストールに基づいて変更された場合)、これは理にかなっています。
これらのシステムカタログビューのいずれかでフィールドのソースを表示する場合は、次の操作を行います。
システムカタログビューの1つから1つのフィールドを選択してクエリを実行します(実行プランは1つのフィールドだけでも途方もなく大きく/複雑であり、多くのフィールドへの参照が含まれるため、一度に1つのフィールドのみを選択することをお勧めします。 t選択):
_SELECT recovery_model_desc FROM sys.databases;
_
Execution plan.xml
_Execution plan.xml
_タブに移動します<OutputList>
_タグの最初の出現を探します(通常は10行目と20行目の間にある必要があります)<ColumnReference>
_タグが必要です。そのタグの属性は、テーブルの特定のフィールドを指すか、計画の後半で定義された式を指す必要があります。属性が実際のフィールドを指している場合、それはすべての情報を持っているので完了です。以下は、_recovery_model_desc
_フィールドの表示です。
_<ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
Table="[syspalvalues]" Alias="[ro]" Column="name" />
_
代わりに_state_desc
_フィールドを選択した場合など、属性が式を指す場合、最初に次のことがわかります。
_<ColumnReference Column="Expr1024" />
_
この場合、_Expr1024
_の定義やそれが思いつく#ものについて、残りの計画を調べる必要があります。これらの参照がいくつか存在する可能性があることに注意してください。ただし、定義は_<OutputList>
_ブロックには含まれません。ただし、その定義を含む_<ScalarOperator>
_兄弟要素があります。以下は、_state_desc
_フィールドの表示です。
_<ScalarOperator ScalarString="CASE WHEN serverproperty('EngineEdition')=5 AND [Expr1081]=(1) THEN N'RESTORING' ELSE CASE WHEN serverproperty('EngineEdition')=5 AND CONVERT(bit,[master].[sys].[sysdbreg].[status] as [d].[status]&(128),0)=(1) THEN N'COPYING' ELSE CASE WHEN serverproperty('EngineEdition')=5 AND CONVERT(bit,[master].[sys].[sysdbreg].[status] as [d].[status]&(256),0)=(1) THEN N'SUSPECT' ELSE [mssqlsystemresource].[sys].[syspalvalues].[name] as [st].[name] END END END">
_
データベースレベルのカタログビューのソースを確認する場合も同様です。 _sys.tables
_のようなオブジェクトに対してこれを行うと、name
フィールドが_[current_db].[sys].[sysschobjs]
_(データベースの照合と照合が一致する理由)から取得されることが示されますが、_lock_escalation_desc
_フィールドは_[mssqlsystemresource].[sys].[syspalvalues]
_から取得されます(これが_Latin1_General_CI_AS_KS_WS
_の照合順序を持つ理由です)。
照合の優先順位とは何か、どのように機能するかがわかったところで、その知識をUNPIVOTクエリに適用してみましょう。
UNPIVOT
操作の場合、SQL Serverは各ソースフィールドが完全に同じ型であることを本当に好むようです(つまり、必要です)。通常、「タイプ」は基本タイプ(つまりVARCHAR
/NVARCHAR
/INT
/など)を指しますが、ここではSQL ServerにもCOLLATIONが含まれています。これは、照合が制御するもの、VARCHARの文字セット(つまり、コードページ)、および文字の等価性と文字の組み合わせ(つまり、正規化)を決定する言語規則を考えると、不当なものと見なされるべきではありません。以下は、Unicodeの「正規化」とは何かに関するmimi-primerです。
_PRINT '---';
IF (N'aa' COLLATE Danish_Greenlandic_100_CI_AI = N'å' COLLATE Danish_Greenlandic_100_CI_AI)
PRINT 'Danish_Greenlandic_100_CI_AI';
IF (N'aa' COLLATE SQL_Latin1_General_CP1_CI_AI = N'å' COLLATE SQL_Latin1_General_CP1_CI_AI)
PRINT 'SQL_Latin1_General_CP1_CI_AI';
PRINT '---';
IF (N'of' COLLATE Upper_Sorbian_100_CI_AI = N'öf' COLLATE Upper_Sorbian_100_CI_AI)
PRINT 'Upper_Sorbian_100_CI_AI';
IF (N'of' COLLATE German_PhoneBook_CI_AI = N'öf' COLLATE German_PhoneBook_CI_AI)
PRINT 'German_PhoneBook_CI_AI';
PRINT '---';
_
戻り値:
_---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---
_
それでは、元のクエリを開始しましょう。さまざまな変更が結果をどのように変更するかを確認するためにいくつかのテストを行い、次にいくつかの変更だけでそれを修正できることを確認します。
最初のエラーは、ピボット解除される2番目のフィールドであるCompatibilityLevel
フィールドに関するもので、たまたますべての文字列リテラルを含む式です。フィールド参照がない場合、結果の照合は「強制可能なデフォルト」と見なされます)。強制可能なデフォルトは、現在のデータベースの照合を引き受けます。たとえば、_SQL_Latin1_General_CP1_CI_AS
_とします。次の20ほどのフィールドも文字列リテラルのみであるため、強制可能なデフォルトであるため、競合しないようにする必要があります。しかし、最初のフィールド_recovery_model_desc
_を振り返ると、これは_sys.databases
_のフィールドから直接取得されているため、「暗黙的な」照合になり、notになります。ローカルDBの照合順序を使用しますが、代わりに元の照合順序、つまり_Latin1_General_CI_AS_KS_WS
_を保持します(実際には_[mssqlsystemresource]
_ DBからのものであるため)。
したがって、フィールド1(RecoveryModel)が_Latin1_General_CI_AS_KS_WS
_であり、フィールド2(CompatibilityLevel)が_SQL_Latin1_General_CP1_CI_AS
_である場合、フィールド2を_Latin1_General_CI_AS_KS_WS
_にして、フィールド1と一致させることができます。次に、フィールド3(AutoClose)のエラーが表示されます。
以下をCompatibilityLevel
行の最後に追加します。
_COLLATE Latin1_General_CI_AS_KS_WS
_
次にクエリを実行します。案の定、エラーは、競合しているのはAutoClose
フィールドであることを示しています。
2番目のテストでは、今行った変更を元に戻す必要があります(つまり、COLLATE
行の末尾からCompatibilityLevel
句を削除します。
これで、SQL Serverがフィールドが指定された順序で真に評価している場合、フィールド1(RecoveryModel)を削除できるはずです。これにより、現在のフィールド2(CompatibilityLevel)がマスター照合を設定するフィールドになります。結果のUNPIVOT。また、CompatibilityLevel
フィールドはデータベースの照合順序をとる強制可能なデフォルトであるため、最初のエラーは、フィールド参照であるPageVerify
フィールドである必要があります。これは、オリジナルを保持する暗黙的な照合順序です。照合。この場合は_Latin1_General_CI_AS_KS_WS
_であり、現在のDBの照合ではありません。
したがって、SELECT
の_, RecoveryModel
_で始まる行を(先頭に向かって)コメントアウトしてから、下のRecoveryModel
句のUNPIVOT
行をコメントアウトします。 CompatibilityLevel
の次の行から先頭のコンマを削除して、構文エラーが発生しないようにします。
そのクエリを実行します。案の定、エラーは、競合しているのはPageVerify
フィールドであることを示しています。
3番目のテストでは、RecoveryModel
フィールドを削除するために行った変更を元に戻す必要があります。したがって、先に進んでコンマを元に戻し、他の2つの行のコメントを外してください。
これで、照合を強制して別の方向に進むことができます。強制デフォルトの照合フィールド(ほとんどのフィールド)の照合を変更するのではなく、暗黙的な照合フィールドを現在のDBの照合に変更できますか?
したがって、最初のテストと同様に、明示的なCOLLATE
句を使用して、フィールド1(RecoveryModel)の照合を強制できます。ただし、特定の照合を指定してから、別の照合を使用してデータベースでクエリを実行すると、強制デフォルトの照合フィールドは新しい照合を取得し、この最初のフィールドの設定と競合します。それは苦痛のようです。幸いなことに、これに対処する動的な方法があります。現在のデータベースの照合を取得する_DATABASE_DEFAULT
_という疑似照合があります(coercible-defaultフィールドのように)。
先に進み、_, RecoveryModel
_で始まる行の最後の上部に、以下を追加します:_COLLATE DATABASE_DEFAULT
_
そのクエリを実行します。案の定、エラーは再び、競合があるのはPageVerify
フィールドであることを示しています。
最終テストでは、以前の変更を元に戻す必要はありません。
このUNPIVOT
クエリを修正するために必要なのは、_COLLATE DATABASE_DEFAULT
_を残りの暗黙的な照合フィールドの最後に追加することだけです:PageVerify
およびRestrictedAccess
。 Collation
フィールドも暗黙的な照合ですが、そのフィールドはmaster
データベースから取得されます。これは通常、「現在の」データベースと一致しています。ただし、これが常に機能するように安全にしたい場合は、そのフィールドの最後にも_COLLATE DATABASE_DEFAULT
_を追加してください。
そのクエリを実行します。案の定、エラーはありません。このクエリを修正するために必要なのは、_COLLATE DATABASE_DEFAULT
_を3つのフィールドの最後(必須)と、おそらく1つ(オプション)に追加することだけでした。
オプションのテスト:UNPIVOTクエリが正しく機能するようになったので、次のように、CONVERT(VARCHAR(50),
で始まるフィールド定義の1つだけを_51
_に変更します。CONVERT(VARCHAR(51),
。
クエリを実行します。一致しないのは照合順序のみの場合と同じ_The type of column "X" conflicts with the type of other columns specified in the UNPIVOT list.
_エラーが表示されます。
データ型と照合の不一致の両方で同じエラーを取得することは、本当に役立つほど具体的ではありません。ですから、改善の余地は確かにあります:)。
照合に関する特定の質問よりもクエリに関連する注意:
すべてのソースフィールドはデータ型NVARCHAR
であるため、CONVERT
ではなくNVARCHAR
にすべての出力フィールドをVARCHAR
するほうが安全です。現在、非標準のASCII文字が含まれているデータを処理していない可能性がありますが、システムメタデータはそれらを許可しているため、NVARCHAR(128)
に変換します-これは、任意の文字列の最大長ですこれらのフィールドのうち、少なくとも将来的に問題が発生しないこと、またはこのコードをコピーする他の誰かがシステムにこれらの文字をすでに持っている可能性があることを保証します。
これは、質問に対する完全な回答ではなく、特定の問題の回避策です。エラーを回避するには、varchar(50)
ではなく_sql_variant
_に変換します。
_DECLARE @dbname SYSNAME;
SET @dbname = DB_NAME();
SELECT [Database] = unpvt.DatabaseName
, [Configuration Item] = unpvt.OptionName
, [Configuration Value] = unpvt.OptionValue
, [BaseType] = SQL_VARIANT_PROPERTY(unpvt.OptionValue, 'BaseType')
, [MaxLength] = SQL_VARIANT_PROPERTY(unpvt.OptionValue, 'MaxLength')
, [Collation] = SQL_VARIANT_PROPERTY(unpvt.OptionValue, 'Collation')
FROM (
SELECT
DatabaseName = name
, RecoveryModel = CONVERT(sql_variant, d.recovery_model_desc)
, CompatibilityLevel = CONVERT(sql_variant, CASE d.[compatibility_level] WHEN 70 THEN 'SQL Server 7' WHEN 80 THEN 'SQL Server 2000' WHEN 90 THEN 'SQL Server 2005' WHEN 100 THEN 'SQL Server 2008' WHEN 110 THEN 'SQL Server 2012' WHEN 120 THEN 'SQL Server 2014' ELSE 'UNKNOWN' END)
, AutoClose = CONVERT(sql_variant, CASE d.is_auto_close_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoCreateStatistics = CONVERT(sql_variant, CASE d.is_auto_create_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoShrink = CONVERT(sql_variant, CASE d.is_auto_shrink_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoUpdateStatistics = CONVERT(sql_variant, CASE d.is_auto_update_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, AutoUpdateStatisticsAsynch = CONVERT(sql_variant, CASE d.is_auto_update_stats_async_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, CloseCursorOnCommit = CONVERT(sql_variant, CASE d.is_cursor_close_on_commit_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DefaultCursor = CONVERT(sql_variant, CASE d.is_local_cursor_default WHEN 1 THEN 'LOCAL' ELSE 'GLOBAL' END)
, ANSINULL_Default = CONVERT(sql_variant, CASE d.is_ansi_null_default_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSINULLS_Enabled = CONVERT(sql_variant, CASE d.is_ansi_nulls_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSIPadding_Enabled = CONVERT(sql_variant, CASE d.is_ansi_padding_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ANSIWarnings_Enabled = CONVERT(sql_variant, CASE d.is_ansi_warnings_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ArithmeticAbort_Enabled = CONVERT(sql_variant, CASE d.is_arithabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, ConcatNullYieldsNull = CONVERT(sql_variant, CASE d.is_concat_null_yields_null_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, CrossDBOwnerChain = CONVERT(sql_variant, CASE d.is_db_chaining_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DateCorrelationOptimized = CONVERT(sql_variant, CASE d.is_date_correlation_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, NumericRoundAbort = CONVERT(sql_variant, CASE d.is_numeric_roundabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, [Parameterization] = CONVERT(sql_variant, CASE d.is_parameterization_forced WHEN 0 THEN 'SIMPLE' ELSE 'FORCED' END)
, QuotedIdentifiers_Enabled = CONVERT(sql_variant, CASE d.is_quoted_identifier_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, RecursiveTriggers_Enabled = CONVERT(sql_variant, CASE d.is_recursive_triggers_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, [TrustWorthy] = CONVERT(sql_variant, CASE d.is_trustworthy_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, VARDECIMAL_Storage = CONVERT(sql_variant, 'TRUE')
, PageVerify = CONVERT(sql_variant, page_verify_option_desc )
, BrokerEnabled = CONVERT(sql_variant, CASE d.is_broker_enabled WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, DatabaseReadOnly = CONVERT(sql_variant, CASE d.is_read_only WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, EncryptionEnabled = CONVERT(sql_variant, CASE d.is_encrypted WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
, RestrictedAccess = CONVERT(sql_variant, user_access_desc)
, Collation = CONVERT(sql_variant, d.collation_name)
FROM sys.databases d
WHERE name = @dbname
OR @dbname IS NULL
) src
UNPIVOT
(
OptionValue FOR OptionName IN
(
RecoveryModel
, CompatibilityLevel
, AutoClose
, AutoCreateStatistics
, AutoShrink
, AutoUpdateStatistics
, AutoUpdateStatisticsAsynch
, CloseCursorOnCommit
, DefaultCursor
, ANSINULL_Default
, ANSINULLS_Enabled
, ANSIPadding_Enabled
, ANSIWarnings_Enabled
, ArithmeticAbort_Enabled
, ConcatNullYieldsNull
, CrossDBOwnerChain
, DateCorrelationOptimized
, NumericRoundAbort
, [Parameterization]
, QuotedIdentifiers_Enabled
, RecursiveTriggers_Enabled
, [TrustWorthy]
, VARDECIMAL_Storage
, PageVerify
, BrokerEnabled
, DatabaseReadOnly
, EncryptionEnabled
, RestrictedAccess
, Collation
)
) AS unpvt;
_
OptionValue
列の基礎となるタイプに関する情報のために3つの列を追加しました。
クライアントが_sql_variant
_データを処理できない場合は、_unpvt.OptionValue
_列で最終(トップレベル)変換を実行して、たとえばnvarchar(256)
。
わかりましたので、私は見てみました
sp_helptext [sys.databases]
それから、柱がどこから来ているのかを壊しました。 Latin1_General_CI_AS_KS_WS
照合順序を持つものはすべて、汎用ルックアップテーブルのように見えるシステムテーブルsys.syspalvalues
から取得されます(これはシステムテーブルであるため、DACを介して接続する必要があります。 。)。
私の推測では、可能なルックアップ値を処理するためにLatin1_General_CI_AS_KS_WS
に設定されています。しかし、それがいかに煩わしいかはわかります。
定義を確認する別の方法(元はコメントで Max によって提供されていました)は次のとおりです。
SELECT ObjectSchema = s.name
, ObjectName = o.name
, ObjectDefinition = sm.definition
FROM master.sys.all_sql_modules sm
INNER JOIN master.sys.system_objects o ON sm.object_id = o.object_id
INNER JOIN master.sys.schemas s ON o.schema_id = s.schema_id
WHERE s.name = 'sys'
AND o.name = 'databases';`