Що стосується зіставлення деяких стовпців у sys.databases?


21

Я намагаюся запустити UNPIVOTрізні колонки, що містяться в sys.databasesрізних версіях SQL Server, починаючи з 2005 по 2012 рік.

Не UNPIVOTвдалося отримати таке повідомлення про помилку:

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

Ви отримуєте цю помилку:

Msg 451, рівень 16, стан 1
Неможливо вирішити конфлікт зіставлення між "Albanian_BIN" та "SQL_Estonian_CP1257_CS_AS" в операторі UNION ALL, що виникає в колонці 1 оператора SELECT.

Аналогічно, якщо ви намагаєтеся виконати 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
  • sadržament_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)

Тут починає надходити прецедент Collation . Обидва варіанти виводу тут - це системні функції: serverproperty()і collationpropertyfromid(). Порівняння цього виразу вважається "примусовим за замовчуванням":

Будь-яка змінна рядок символу 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пунктом, оскільки це має найвищий пріоритет:

Явний = Вираз, який явно передається конкретному зіставленню, використовуючи в виразі пункт COLLATE.

Явне має перевагу над неявним. Імпліцит має перевагу над Coercible-за замовчуванням:
Явне> Неявне> Coercible-default

І пов'язане питання:

Чому порівняння цих стовпців встановлюється статично?

Справа не в тому, що вони статично задані, а також, що інші поля якимось чином динамічні. Усі поля у всіх цих переглядах системного каталогу працюють за тими ж правилами прецедентності зіставлення. Причиною того, що вони здаються "статичнішими", ніж інші поля (тобто вони не змінюються, навіть якщо встановити SQL Server з іншим співставленням за замовчуванням, що, в свою чергу, створює системні бази даних із цим зіставленням за замовчуванням), полягає в тому, що [mssqlsystemresource]база даних послідовно має порівняння Latin1_General_CI_AS_KS_WSдля будь-якої інсталяції SQL Server (або так, безумовно, це з'являється). І це має сенс, тому що в іншому випадку SQL Server буде важко керувати собою внутрішньо (тобто якщо правила сортування та порівняння, використовувані для внутрішньої логіки, змінюються на основі встановлення).

Як бачити цю специфіку самостійно

Якщо ви хочете побачити джерело будь-яких полів у будь-якому з цих переглядів системного каталогу, просто виконайте наступне:

  1. На вкладці запитів у SSMS увімкніть опцію запиту "Включити фактичний план виконання" ( CTRL-M)
  2. Виконайте запит, вибравши одне поле з одного із представлень системного каталогу (я рекомендую вибирати лише одне поле за один раз, оскільки план виконання є смішно великим / складним навіть для одного поля, і буде містити посилання на багато полів, які ви не є ' t вибір):

    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-запит".

Тепер, коли ми знаємо, що таке «Colced Precedence» і як це працює, давайте застосуємо ці знання до запиту UNPIVOT.

Для UNPIVOTроботи SQL Server , здається, дійсно вважає за краще ( це означає: потрібно) , що кожне поле джерела мати точно такий же типу . Зазвичай "тип" відноситься до базового типу (тобто VARCHAR/ NVARCHAR/ INT/ тощо), але тут SQL Server також включає КОЛЯЦІЮ. Це не слід сприймати як нерозумне, враховуючи те, що контролює 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поля, яке є другим полем, яке не підлягає передачі, і просто відбувається вираз, що містить усі рядкові букви. Без посилань на поле, отримане порівняння вважається "примусовим за замовчуванням"). Скажімо, примусові налаштування беруть на себе порівняння поточної бази даних SQL_Latin1_General_CP1_CI_AS. Наступні 20 або більше полів також є лише рядковими літералами, а отже, також є примусовими за замовчуванням, тому вони не повинні конфліктувати. Але якщо ми оглянемось на перше поле, recovery_model_descто воно надходить безпосередньо з поля в 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 (Автозакриття).

    До кінця 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а яке не є порівняння поточної БД.

    Так що вперед і закоментуйте рядок , що починається з , RecoveryModelв SELECT( в напрямку зверху) , а потім закоментуйте RecoveryModelрядок в UNPIVOTпункті нижче , і зніміть провідну кому з наступного рядка для CompatibilityLevelтак що ви не отримаєте синтаксичну помилку.

    Запустіть цей запит. Напевно, помилка тепер говорить, що саме PageVerifyполе має конфлікт.

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

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

    Таким чином, як і наш перший тест, ми повинні мати можливість примусити зіставити поле 1 (RecoveryModel) з явним COLLATEпунктом. Але якщо ми вкажемо конкретне зіставлення, а потім запустимо запит у базі даних з іншим зіставленням, поля зіставлення за замовчуванням підберуть новий збір, який потім буде конфліктувати з тим, для чого ми задаємо це перше поле. Це здається болем. На щастя, існує динамічний спосіб впоратися з цим. Існує псевдозбір, який називається DATABASE_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).


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';`
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.