Что случилось с сопоставлением некоторых столбцов в sys.databases?

21

Я пытаюсь запустить UNPIVOTна различных столбцах, содержащихся вsys.databases разных версиях SQL Server, в период с 2005 по 2012 год.

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сопоставлением, оператор успешно выполняется. Если я изменю T-SQL, чтобы в некоторых полях было COLLATEпредложение, он будет работать на серверах с другими параметрами сортировки.

Код, который работает на серверах с параметрами сортировки, отличными от 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;

Наблюдаемое поведение состоит в том, что в следующих полях не наблюдается ни сопоставление сервера, ни сопоставление базы данных; они всегда представлены в Latin1_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;

Результаты:

введите описание изображения здесь

Почему сопоставление этих столбцов статически установлено?

Макс Вернон
источник

Ответы:

17

Официальное слово от Microsoft:

Некоторые из столбцов, которые содержат предопределенные строки (например, типы, системные описания и константы), всегда привязаны к определенному сопоставлению - 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 Server с SQL_Estonian_CP1257_CS_ASсопоставлением, выполните следующее:

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

Теперь, чтобы продемонстрировать представления метаданных, которые наследуют параметры сортировки базы данных, а не наследуют параметры сортировки сервера из основной базы данных:

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
Не удается разрешить конфликт сопоставления между «Albanian_BIN» и «SQL_Estonian_CP1257_CS_AS» в операторе UNION ALL, встречающемся в столбце оператора SELECT 1.

Точно так же, если вы попытаетесь выполнить 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;

Единственный раз, когда это может вызвать проблемы, вы получите сбивающий с толку вывод, если сопоставление принудительно, но не содержит того же представления символов, или если используется сортировка и принудительное сопоставление использует другой порядок сортировки, чем в источнике.

Аарон Бертран
источник
7

Справочная информация о порядке сортировки

Поведение, которое вы наблюдаете в отношении сопоставления различных полей в представлениях системного каталога, является результатом того, как определено каждое поле, и приоритета сопоставления.

При взгляде на sys.databases важно помнить, что это не стол. В то время как в прошлом (я думаю , заканчиваясь в SQL Server 2000) это была система каталога таблица , теперь они системный каталог просмотры . Следовательно, источник информации в них не обязательно исходит из текущего контекста базы данных (или контекста указанной базы данных при работе с полностью определенным объектом, таким какmaster.sys.databases ).

Имея дело конкретно с sys.databases , некоторые поля поступают из [master]базы данных (которая была создана с сопоставлением на основе сопоставления по умолчанию экземпляра - т.е. сопоставления на уровне сервера), некоторые поля являются выражениями (то есть CASEутверждениями), а некоторые приходят из «скрытого» источника: [mssqlsystemresource]базы данных. И [mssqlsystemresource]база данных имеет сопоставление:Latin1_General_CI_AS_KS_WS .

nameПоле поступает из nameполя master.sys.sysdbreg. Таким образом, это поле всегда должно быть в сопоставлении[master] базы данных, которое снова будет соответствовать сопоставлению сервера.

НО, следующие поля в sys.databases входят из [name]поля в [mssqlsystemresource].[sys].[syspalvalues]:

  • user_access_desc
  • snapshot_isolation_state_desc
  • recovery_model_desc
  • page_verify_option_desc
  • log_reuse_wait_desc
  • containment_desc

Эти поля всегда должны иметь параметры сортировки 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)

Вот тут-то и начинает действовать приоритетность сортировки . Оба варианта вывода здесь являются системными функциями: serverproperty()и collationpropertyfromid(). Сортировка этого выражения считается «Coercible-default»:

Любая символьная строковая переменная Transact-SQL, параметр, литерал или выходные данные встроенной функции каталога или встроенной функции, которая не принимает строковые входные данные, но создает строковые выходные данные.

Если объект объявлен в пользовательской функции, хранимой процедуре или триггере, объекту назначается сопоставление по умолчанию для базы данных, в которой создается функция, хранимая процедура или триггер. Если объект объявлен в пакете, ему назначается сопоставление по умолчанию для текущей базы данных для соединения.

В свете этого второго абзаца, поскольку 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. Почему? Что-то новое вводится в это выражение: ссылка на реальное поле: [mssqlsystemresource].[sys].[syspalvalues].[name]в этом последнем ELSEпредложении. Ссылки на столбцы считаются «неявными»:

Ссылка на столбец. Сопоставление выражения берется из сопоставления, определенного для столбца в таблице или представлении.

Конечно, это вызывает интересный вопрос: возможно ли, чтобы это выражение возвращало различные параметры сортировки в зависимости от того, как 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]принимают сопоставление[msdb] базы данных (которая, будучи системной БД, определялась параметрами сортировки сервера).

Возвращаясь к первоначальному вопросу

Наблюдаемое поведение состоит в том, что в следующих полях не наблюдается ни сопоставление сервера, ни сопоставление базы данных; они всегда представлены в Latin1_General_CI_AS_KS_WSсопоставлении.

Ваше наблюдение точно: эти поля всегда имеют параметры сортировки Latin1_General_CI_AS_KS_WS, независимо от параметров сортировки сервера или базы данных. И причина в том, что: Приоритет сортировки. Эти поля берутся из таблицы в [mssqlsystemresource]базе данных и сохранят это исходное сопоставление, если не будут переопределены явным COLLATEпредложением, поскольку оно имеет наивысший приоритет:

Explicit = Выражение, которое явно приведено к определенному сопоставлению с помощью предложения COLLATE в выражении.

Явный имеет приоритет над неявным. Неявный имеет приоритет над Coercible-default:
Явный> Неявный> Coercible-default

И связанный с этим вопрос:

Почему сопоставление этих столбцов статически установлено?

Дело не в том, что они статически заданы, а в том, что другие поля как-то динамичны. Все поля во всех этих представлениях системного каталога работают по одним и тем же правилам приоритета сортировки. Причина того, что они кажутся более «статичными», чем другие поля (то есть они не изменяются, даже если вы устанавливаете SQL Server с другим сопоставлением по умолчанию, которое, в свою очередь, создает системные базы данных с таким сопоставлением по умолчанию), заключается в том, что [mssqlsystemresource]база данных согласованно имеет параметры сортировки в Latin1_General_CI_AS_KS_WSлюбой установке SQL Server (или так оно и есть). И это имеет смысл, потому что в противном случае SQL Server будет трудно управлять собой внутри себя (т. Е. Если правила сортировки и сравнения, используемые для внутренней логики, изменяются в зависимости от установки).

Как увидеть эти особенности самостоятельно

Если вы хотите увидеть источник какого-либо поля в любом из этих представлений системного каталога, просто сделайте следующее:

  1. На вкладке запроса в SSMS включите параметр запроса «Включить фактический план выполнения» ( CTRL-M)
  2. Выполните запрос, выбрав одно поле из одного из представлений системного каталога (я рекомендую выбирать только одно поле за раз, поскольку план выполнения смехотворно велик / сложен даже для всего одного поля и будет включать ссылки на многие поля, которые вам не нужны). т выбор):

    SELECT recovery_model_desc FROM sys.databases;
  3. Перейти на вкладку «План выполнения»
  4. Щелкните правой кнопкой мыши в графической области плана выполнения и выберите «Показать XML плана выполнения ...»
  5. Откроется новая вкладка в SSMS с названием, похожим на: Execution plan.xml
  6. Перейти на Execution plan.xmlвкладку
  7. Найдите первое вхождение <OutputList>тега (обычно оно должно быть между строками 10 и 20)
  8. Там должен быть <ColumnReference>тег. Атрибуты в этом теге должны либо указывать на конкретное поле в таблице, либо указывать на выражение, определенное позже в плане.
  9. Если атрибуты указывают на реальное поле, то все готово, поскольку в нем есть вся информация. Следующее - то, что обнаруживается для recovery_model_descобласти:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
  10. Если атрибуты указывают на выражение, например, если вы вместо этого выбрали state_descполе, то вы сначала найдете:

    <ColumnReference Column="Expr1024" />
  11. В этом случае вам нужно просмотреть остальную часть плана на предмет определения 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]&amp;(128),0)=(1) THEN N'COPYING' ELSE CASE WHEN serverproperty('EngineEdition')=5 AND CONVERT(bit,[master].[sys].[sysdbreg].[status] as [d].[status]&amp;(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.

Для UNPIVOTоперации SQL Server действительно предпочитает (то есть: требует), чтобы каждое поле источника было точно такого же типа . Обычно «тип» относится к базовому типу (т.е. VARCHAR/ NVARCHAR/ INT/ и т. Д.), Но здесь SQL Server также включает в себя COLLATION. Это не следует рассматривать как необоснованное, учитывая то, что контролирует Collations: набор символов (т. Е. Кодовая страница) для VARCHAR и лингвистические правила, определяющие эквивалентность символов и комбинации символов (т. Е. Нормализация). Ниже приводится краткий пример того, что такое Unicode «нормализация»:

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
---

Итак, теперь давайте начнем ваш оригинальный запрос. Мы проведем несколько тестов, чтобы увидеть, как различные изменения влияют на результат, а затем мы увидим, как всего несколько изменений могут это исправить.

  1. Первая ошибка касается CompatibilityLevelполя, которое является вторым полем, которое не должно быть развернуто, и просто является выражением, содержащим все строковые литералы. Без ссылок на поля результирующее сопоставление считается «coercible-default»). Допустим, Coercible-defaults принимает параметры сортировки текущей базы данных SQL_Latin1_General_CP1_CI_AS. Следующие 20 или около того полей также являются строковыми литералами и, следовательно, также являются принудительными значениями по умолчанию, поэтому они не должны конфликтовать. Но если мы оглянемся на первое поле, recovery_model_descоно приходит непосредственно из поля in sys.databases, что делает его «неявным» сопоставлением, и это не берет на себя сопоставление локальной БД, а вместо этого сохраняет свое исходное сопоставление, которое является Latin1_General_CI_AS_KS_WS( потому что это действительно исходит из [mssqlsystemresource]БД).

    Итак, если поле 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поле является coercible-default, которое принимает параметры сортировки базы данных, поэтому первой ошибкой должно быть PageVerifyполе, являющееся ссылкой на поле, которое представляет собой неявное сопоставление, сохраняющее исходное сопоставление, которое в данном случае является Latin1_General_CI_AS_KS_WSи не является сопоставление текущей БД.

    Так что вперед и закомментируйте строку , начинающуюся с , RecoveryModelв SELECT( в направлении сверху) , а затем закомментируйте RecoveryModelстроку в UNPIVOTпункте ниже , и снимите ведущую запятую из следующей строки для CompatibilityLevelтак что вы не получите синтаксическую ошибку.

    Запустите этот запрос. Конечно, ошибка теперь гласит, PageVerifyчто конфликт имеет именно поле.

  3. Для нашего третьего теста нам нужно отменить изменения, которые мы только что сделали, чтобы удалить RecoveryModelполе. Так что давай, вставь запятую и раскомментируй эти две другие строки.

    Теперь мы можем пойти в другом направлении с помощью сопоставления. Вместо того, чтобы изменять параметры сортировки полей параметров сопоставления по умолчанию (которые являются большинством из них), мы должны иметь возможность изменить поля неявных параметров сопоставления для полей текущей базы данных, верно?

    Итак, так же, как и в нашем первом тесте, мы должны иметь возможность принудительно сопоставить поле 1 (RecoveryModel) с явным COLLATEпредложением. Но если мы укажем конкретное сопоставление и затем запустим запрос в базе данных с другим сопоставлением, поля сопоставления по умолчанию по принуждению получат новое сопоставление, которое затем будет конфликтовать с тем, что мы устанавливаем в этом первом поле. Это похоже на боль. К счастью, есть динамичный способ справиться с этим. Существует DATABASE_DEFAULTпсевдосбор, который выбирает текущие параметры сортировки баз данных (как это делают поля coercible-default).

    Идите вперед и добавьте следующее в конец строки, к началу, которая начинается с , RecoveryModel: COLLATE DATABASE_DEFAULT

    Запустите этот запрос. Конечно же, ошибка еще раз утверждает, PageVerifyчто конфликт имеет именно поле.

  4. Для финального теста нам не нужно отменять какие-либо предыдущие изменения.

    Все, что нам нужно сделать сейчас, чтобы исправить этот UNPIVOTзапрос, это добавить COLLATE DATABASE_DEFAULTв конец оставшиеся неявные поля сопоставления: PageVerifyи RestrictedAccess. Хотя Collationполе также является неявным сопоставлением, это поле происходит из masterбазы данных, которая обычно соответствует «текущей» базе данных. Но если вы хотите быть в безопасности, чтобы это всегда работало, добавьте также COLLATE DATABASE_DEFAULTи конец этого поля.

    Запустите этот запрос. Конечно же, без ошибок. Все, что нужно для исправления этого запроса, это добавление COLLATE DATABASE_DEFAULTв конец 3 полей (обязательно) и, возможно, еще 1 (необязательно).

  5. Дополнительный тест: Теперь, когда мы , наконец, запрос UNPIVOT работает правильно, изменение только одного любого из определений полей , начиная с CONVERT(VARCHAR(50),до , а не быть 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)- что является наибольшей максимальной длиной любого из этих полей - по крайней мере гарантирует, что в будущем не возникнет проблем для вас или для любого другого, кто копирует этот код, у которого уже могут быть некоторые из этих символов в их системе.

Соломон Руцкий
источник
5

Это обходной путь для конкретной проблемы, а не полный ответ на вопрос. Вы можете избежать ошибки, преобразовав sql_variantвместо varchar(50):

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столбца.

Образец вывода

Если клиент не может обработать sql_variantданные, выполните окончательное (верхний уровень) преобразование в unpvt.OptionValueстолбце, например, в nvarchar(256).

Пол Уайт говорит, что GoFundMonica
источник
4

Итак, я взглянул на

sp_helptext [sys.databases]

затем сломался, откуда пришли колонны. Все с Latin1_General_CI_AS_KS_WSсопоставлением происходят из системной таблицы, sys.syspalvaluesкоторая выглядит как общая справочная таблица (это системная таблица, поэтому вам нужно будет подключиться через ЦАП, чтобы ее увидеть).

Я предполагаю, что он настроен Latin1_General_CI_AS_KS_WSна обработку любых возможных значений поиска. Я вижу, как это будет раздражать, хотя.

Еще один способ увидеть определение (первоначально предоставленное Максом в комментарии):

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';`
Кеннет Фишер
источник