Ada apa dengan pengumpulan beberapa kolom di sys.databases?

21

Saya mencoba menjalankan UNPIVOTberbagai kolom yang ada disys.databases berbagai versi SQL Server, mulai dari 2005 hingga 2012.

The UNPIVOTgagal dengan pesan error berikut:

Msg 8167, Level 16, State 1, Line 48

Jenis kolom "CompatibilityLevel" bertentangan dengan jenis kolom lain yang ditentukan dalam daftar 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;

Ini dirancang untuk memberikan daftar opsi database yang diformat dengan baik untuk database yang diberikan, mirip dengan:

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

Ketika saya menjalankan ini di server dengan Latin1_General_CI_AS_KS_WScollation, pernyataan berhasil. Jika saya memodifikasi T-SQL sehingga bidang-bidang tertentu memiliki COLLATEklausa, itu akan berjalan pada server yang memiliki kumpulan lainnya.

Kode yang berfungsi di server dengan koleksi selain Latin1_General_CI_AS_KS_WSadalah:

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;

Perilaku yang diamati adalah bahwa bidang-bidang berikut ini tidak mengamati susunan server, atau susunan basis data; mereka selalu disajikan dalamLatin1_General_CI_AS_KS_WS susunan.

Pada SQL Server 2012, kita bisa menggunakan sys.sp_describe_first_result_setuntuk dengan mudah memperoleh metadata tentang kolom yang dikembalikan dari permintaan tertentu. Saya menggunakan yang berikut ini untuk menentukan ketidaksesuaian susunan:

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;

Hasil:

masukkan deskripsi gambar di sini

Mengapa susunan kolom-kolom ini diatur secara statis?

Max Vernon
sumber

Jawaban:

17

Kata resmi dari Microsoft:

Beberapa kolom yang berisi string yang telah ditentukan sebelumnya (seperti jenis, deskripsi sistem, dan konstanta) selalu diperbaiki untuk pemeriksaan tertentu - Latin1_General_CI_AS_KS_WS. Ini terlepas dari instance / database collation. Alasannya adalah bahwa ini adalah sistem metadata (bukan metadata pengguna) dan pada dasarnya string ini diperlakukan tidak peka huruf besar-kecil (seperti kata kunci, jadi selalu Latin).

Kolom lain dalam tabel sistem yang berisi metadata pengguna seperti nama objek, nama kolom, nama indeks, nama login, dll. Ambil contoh atau kumpulan database. Kolom disusun untuk pemeriksaan yang tepat pada saat instalasi SQL Server dalam hal contoh pemeriksaan & pada saat pembuatan database jika terjadi pengumpulan database.

Anda bertanya (beri penekanan pada saya):

Mengapa susunan kolom ini diatur secara statis?

Alasan beberapa kolom diatur secara statis adalah agar kueri tidak perlu khawatir tentang server atau database collation (yang lebih penting: CaSe SenSiTIviTy) agar berfungsi dengan benar. Kueri ini akan selalu berfungsi terlepas dari collation:

SELECT * FROM sys.databases WHERE state_desc = N'ONLine';

Sedangkan jika server collation peka terhadap huruf besar-kecil, kueri di atas akan menghasilkan 0 baris, seperti ini:

  SELECT * FROM sys.databases 
  WHERE state_desc COLLATE Albanian_BIN = N'ONLine';

Misalnya, jika Anda menginstal contoh SQL Server dengan SQL_Estonian_CP1257_CS_AScollation, kemudian jalankan yang berikut:

SELECT name, collation_name 
FROM master.sys.all_columns
WHERE collation_name IS NOT NULL
AND [object_id] = OBJECT_ID(N'sys.databases');

Anda akan melihat hasil ini (atau yang serupa, tergantung pada versi SQL Server Anda):

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

Sekarang, untuk mendemonstrasikan pandangan metadata yang mewarisi susunan basis data, daripada mewarisi susunan server dari basis data induk:

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

Hasil:

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

Jadi, Anda dapat melihat bahwa dalam kasus ini beberapa kolom mewarisi susunan basis data, sementara yang lain diperbaiki pada susunan Latin1 "generik" ini, artinya digunakan untuk mengisolasi nama dan properti tertentu dari masalah sensitivitas kasus seperti dijelaskan di atas.

Jika Anda mencoba melakukan UNION, misalnya:

SELECT name FROM albanian.sys.columns
UNION ALL
SELECT name FROM server_collation.sys.columns;

Anda mendapatkan kesalahan ini:

Msg 451, Level 16, Negara 1
Tidak dapat menyelesaikan konflik pemeriksaan antara "Albanian_BIN" dan "SQL_Estonian_CP1257_CS_AS" di UNION SEMUA operator yang muncul di kolom pernyataan SELECT 1.

Demikian pula, jika Anda mencoba melakukan PIVOTatau UNPIVOT, aturannya bahkan lebih ketat (semua tipe output harus sama persis daripada hanya kompatibel), tetapi pesan kesalahannya jauh kurang membantu, dan bahkan menyesatkan:

Msg 8167, Level 16, Negara 1
Jenis kolom "nama kolom" bertentangan dengan jenis kolom lain yang ditentukan dalam daftar UNPIVOT.

Anda perlu mengatasi kesalahan ini menggunakan COLLATEklausa eksplisit dalam kueri Anda. Misalnya, penyatuan di atas dapat:

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;

Satu-satunya waktu hal ini dapat menyebabkan masalah adalah Anda akan mendapatkan hasil yang membingungkan jika suatu collation dipaksa tetapi tidak mengandung representasi karakter yang sama, atau jika sortasi digunakan dan collation paksa menggunakan urutan sortir yang berbeda dari sumbernya.

Aaron Bertrand
sumber
7

Latar Belakang Penentuan Collation

Perilaku yang Anda lihat berkaitan dengan Penyatuan berbagai bidang dalam tampilan katalog sistem adalah hasil dari bagaimana masing-masing bidang didefinisikan dan Pengambilan Prioritas Kolasi.

Saat melihat sys.databases, penting untuk diingat bahwa itu bukan meja. Sementara di masa lalu (saya pikir berakhir di SQL Server 2000) ini adalah tabel katalog sistem , mereka sekarang pandangan katalog sistem . Oleh karena itu, sumber informasi di dalamnya tidak selalu berasal dari konteks database saat ini (atau konteks database yang ditentukan ketika berhadapan dengan objek yang sepenuhnya memenuhi syarat seperti master.sys.databases).

Berhubungan secara khusus dengan sys.databases, beberapa bidang berasal dari [master]basis data (yang dibuat dengan susunan berdasarkan susunan standar turunan - yaitu susunan tingkat server), beberapa bidang adalah ekspresi ( CASEpernyataan yaitu ), dan beberapa akan datang dari sumber "tersembunyi": [mssqlsystemresource]database. Dan [mssqlsystemresource]database memiliki pengumpulan: Latin1_General_CI_AS_KS_WS.

The namelapangan bersumber dari namelapangan di master.sys.sysdbreg. Jadi bidang ini harus selalu dalam susunan [master]basis data, yang lagi-lagi akan cocok dengan susunan server.

TAPI, bidang-bidang berikut ini sys.databasesberasal dari [name]bidang dalam [mssqlsystemresource].[sys].[syspalvalues]:

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

Bidang-bidang tersebut harus selalu memiliki susunan Latin1_General_CI_AS_KS_WS.

The collation_namelapangan, bagaimanapun, datang dari ekspresi berikut:

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)

Di sinilah Collation Precedence mulai masuk. Kedua opsi untuk keluaran di sini adalah fungsi sistem: serverproperty()dan collationpropertyfromid(). Penyusunan ekspresi ini dianggap sebagai "Coercible-default":

Setiap variabel string karakter Transact-SQL, parameter, literal, atau output dari fungsi bawaan katalog, atau fungsi bawaan yang tidak mengambil input string tetapi menghasilkan output string.

Jika objek dideklarasikan dalam fungsi yang ditentukan pengguna, prosedur tersimpan, atau pemicu, objek tersebut diberikan susunan default dari database di mana fungsi, prosedur tersimpan, atau pemicu dibuat. Jika objek dideklarasikan dalam batch, objek tersebut diberikan susunan default dari database saat ini untuk koneksi.

Mengingat paragraf ke-2 itu, karena sys.databasesmerupakan tampilan yang ada dalam masterbasis data, maka diperlukan pengumpulan masterbasis data (bukan basis data saat ini).

The state_desclapangan juga merupakan ekspresi:

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

Tapi, susunan ekspresi ini Latin1_General_CI_AS_KS_WS. Mengapa? Nah, sesuatu yang baru diperkenalkan dalam ungkapan ini: referensi ke bidang nyata: [mssqlsystemresource].[sys].[syspalvalues].[name]dalam ELSEklausa terakhir . Referensi kolom dianggap "Tersirat":

Referensi kolom. Susunan ekspresi diambil dari susunan yang ditentukan untuk kolom dalam tabel atau tampilan.

Tentu saja, ini membuka pertanyaan yang menarik: mungkinkah ungkapan ini mengembalikan susunan yang berbeda tergantung pada bagaimana CASEpenilaiannya? Literal akan berada di collation dari database di mana objek ini didefinisikan, tetapi ELSEkondisi mengembalikan nilai field yang harus mempertahankan collation aslinya. Untungnya, kita dapat mensimulasikan tes menggunakan sys.dm_exec_describe_first_result_set Fungsi Manajemen Dinamis:

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

Pengembalian (pada contoh yang disiapkan dengan susunan SQL_Latin1_General_CP1_CI_AStetapi berjalan dalam database dengan susunan 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

Di sini kita melihat bahwa dua query yang mereferensikan bidang dalam [msdb]mengambil pemeriksaan [msdb]database (yang, sebagai sistem DB, ditentukan oleh server collation).

Berhubungan kembali dengan Pertanyaan Asli

Perilaku yang diamati adalah bahwa bidang-bidang berikut ini tidak mengamati susunan server, atau susunan basis data; mereka selalu disajikan dalamLatin1_General_CI_AS_KS_WS susunan.

Pengamatan Anda tepat: bidang-bidang itu selalu memiliki susunan Latin1_General_CI_AS_KS_WS, terlepas dari susunan server atau susunan basis data. Dan alasannya adalah: Colled Precedence. Bidang-bidang tersebut berasal dari tabel dalam [mssqlsystemresource]database, dan akan mempertahankan pemeriksaan awal itu kecuali diganti dengan COLLATEklausa eksplisit karena memiliki prioritas tertinggi:

Eksplisit = Ekspresi yang secara eksplisit dilemparkan ke pemeriksaan tertentu dengan menggunakan klausa COLLATE dalam ekspresi.

Eksplisit lebih diutamakan daripada implisit. Implisit lebih diutamakan daripada Coercible-default:
Explicit> Implicit> Coercible-default

Dan pertanyaan terkait:

Mengapa susunan kolom-kolom ini diatur secara statis?

Bukan karena mereka diatur secara statis, atau bahwa bidang lain entah bagaimana dinamis. Semua bidang dalam semua tampilan katalog sistem tersebut beroperasi dengan aturan yang sama dengan Colled Precedence. Alasan bahwa mereka tampaknya lebih "statis" daripada bidang lain (yaitu mereka tidak berubah bahkan jika Anda menginstal SQL Server dengan susunan default yang berbeda, yang pada gilirannya membuat sistem database dengan susunan default itu) adalah bahwa [mssqlsystemresource]database konsisten memiliki susunan Latin1_General_CI_AS_KS_WSdi seluruh instalasi SQL Server (atau begitulah tampaknya). Dan ini masuk akal karena kalau tidak akan sulit bagi SQL Server untuk mengelola sendiri secara internal (yaitu jika aturan penyortiran dan perbandingan yang digunakan untuk logika internal diubah berdasarkan instalasi).

Cara melihat spesifik ini sendiri

Jika Anda ingin melihat sumber bidang apa pun di salah satu tampilan katalog sistem ini, lakukan saja yang berikut ini:

  1. Di tab kueri di SSMS, aktifkan Opsi Kueri "Sertakan Rencana Eksekusi Aktual" ( CTRL-M)
  2. Jalankan permintaan memilih satu bidang dari salah satu tampilan katalog sistem (Saya sarankan memilih hanya satu bidang pada suatu waktu karena rencana pelaksanaannya sangat besar / kompleks bahkan untuk satu bidang saja, dan akan menyertakan referensi ke banyak bidang yang Anda tuju ' t memilih):

    SELECT recovery_model_desc FROM sys.databases;
  3. Buka tab "Rencana eksekusi"
  4. Klik kanan di bidang rencana Eksekusi grafis dan pilih "Tampilkan XML Rencana Eksekusi ..."
  5. Tab baru di SSMS akan terbuka dengan judul yang mirip dengan: Execution plan.xml
  6. Buka Execution plan.xmltab
  7. Cari kemunculan pertama <OutputList>tag (biasanya antara 10 dan 20 baris)
  8. Seharusnya ada <ColumnReference>tag. Atribut dalam tag itu harus menunjuk ke bidang tertentu dalam tabel, atau menunjuk ke ekspresi yang didefinisikan kemudian dalam rencana.
  9. Jika atribut menunjuk ke bidang nyata maka Anda selesai karena memiliki semua info. Berikut ini adalah apa yang muncul untuk recovery_model_descbidang:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
  10. Jika atribut menunjuk ke ekspresi, seperti jika Anda memilih state_descbidang, maka Anda awalnya akan menemukan:

    <ColumnReference Column="Expr1024" />
  11. Dalam hal ini, Anda perlu melihat sisa dari rencana untuk definisi Expr1024atau apa pun yang muncul. Perlu diingat bahwa mungkin ada beberapa referensi ini, tetapi definisi tidak akan berada di <OutputList>blok. Namun, ia akan memiliki <ScalarOperator>elemen saudara yang berisi definisi. Berikut ini adalah apa yang muncul untuk state_descbidang:

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

Hal yang sama dapat dilakukan untuk memeriksa sumber tampilan katalog tingkat database juga. Melakukan ini untuk objek seperti sys.tablesakan menunjukkan bahwa namebidang tersebut berasal [current_db].[sys].[sysschobjs](itulah sebabnya ia memiliki susunan yang cocok dengan susunan basis data), sedangkan lock_escalation_descbidang itu berasal [mssqlsystemresource].[sys].[syspalvalues](itulah sebabnya ia memiliki susunan Latin1_General_CI_AS_KS_WS).

Clippy berkata, "Sepertinya Anda ingin melakukan kueri UNPIVOT."

Sekarang kita tahu mengapa Colled Precedence itu dan bagaimana cara kerjanya, mari kita terapkan pengetahuan itu ke permintaan UNPIVOT.

Untuk UNPIVOToperasi, SQL Server tampaknya benar - benar lebih suka (artinya: mengharuskan) bahwa setiap bidang sumber memiliki tipe yang sama persis . Biasanya "type" mengacu pada tipe dasar (yaitu VARCHAR/ NVARCHAR/ INT/ etc) tetapi di sini SQL Server juga termasuk KOLASI. Ini tidak boleh dilihat sebagai tidak masuk akal mengingat apa yang dikontrol Collations: set karakter (yaitu Code Page) untuk VARCHAR, dan aturan linguistik yang menentukan kesetaraan karakter dan kombinasi karakter (mis. Normalisasi). Berikut ini adalah mimi-primer tentang "normalisasi" 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 '---';

Pengembalian:

---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---

Jadi sekarang mari kita mulai permintaan awal Anda. Kami akan melakukan beberapa tes untuk melihat bagaimana berbagai perubahan mengubah hasil, dan kemudian kita akan melihat bagaimana beberapa perubahan dapat memperbaikinya.

  1. Kesalahan pertama adalah tentang CompatibilityLevelbidang, yang merupakan bidang kedua yang tidak diproteksi, dan kebetulan merupakan ekspresi yang mengandung semua string literal. Tanpa referensi bidang, susunan yang dihasilkan dianggap sebagai "koersibel-default"). Coercible-default mengambil collation dari database saat ini, katakanlah SQL_Latin1_General_CP1_CI_AS. 20 atau lebih bidang berikutnya juga hanya string literal dan karenanya juga koersibel-default, sehingga tidak boleh ada konflik. Tetapi jika kita melihat kembali ke bidang pertama recovery_model_desc,, yang datang langsung dari bidang masuk sys.databases, yang membuatnya menjadi "implisit" pemeriksaan, dan ini tidak mengambil pemeriksaan DB lokal, tetapi tetap mempertahankan pemeriksaan aslinya, yaitu Latin1_General_CI_AS_KS_WS( karena itu benar-benar berasal dari [mssqlsystemresource]DB).

    Jadi, jika bidang 1 (RecoveryModel) adalah Latin1_General_CI_AS_KS_WS, dan bidang 2 (CompatibilityLevel) adalah SQL_Latin1_General_CP1_CI_AS, maka kita harus dapat memaksa bidang 2 Latin1_General_CI_AS_KS_WSagar cocok dengan bidang 1, dan kemudian kesalahan akan muncul untuk bidang 3 (AutoClose).

    Tambahkan yang berikut ke akhir CompatibilityLevelbaris:
    COLLATE Latin1_General_CI_AS_KS_WS

    Dan kemudian jalankan query. Benar saja, kesalahan sekarang menyatakan bahwa itu adalah AutoClosebidang yang memiliki konflik.

  2. Untuk pengujian kedua kami, kami harus membatalkan perubahan yang baru saja kami buat (yaitu menghapus COLLATEklausa dari akhir CompatibilityLevelbaris.

    Sekarang, jika SQL Server benar-benar mengevaluasi dalam urutan bidang yang ditentukan, kita harus dapat menghapus bidang 1 (RecoveryModel), yang akan menyebabkan bidang saat ini 2 (CompatibilityLevel) menjadi bidang yang menetapkan susunan master dari UNPIVOT yang dihasilkan. Dan CompatibilityLevelfield adalah coercible-default yang mengambil collation database, jadi error pertama adalah PageVerifyfield, yang merupakan referensi field, yang merupakan collate implisit yang mempertahankan collation asli, yang dalam kasus ini adalah Latin1_General_CI_AS_KS_WSdan yang bukan merupakan pemeriksaan DB saat ini.

    Jadi pergi ke depan dan komentar pada baris dimulai dengan , RecoveryModeldi SELECT(ke arah atas) dan kemudian komentar pada RecoveryModelbaris di UNPIVOTklausul bawah dan menghapus terkemuka koma dari garis berikut untuk CompatibilityLevelsehingga Anda tidak mendapatkan kesalahan sintaks.

    Jalankan kueri itu. Benar saja, kesalahan sekarang menyatakan bahwa itu adalah PageVerifybidang yang memiliki konflik.

  3. Untuk pengujian ketiga kami, kami harus membatalkan perubahan yang baru saja kami lakukan untuk menghapus RecoveryModelbidang. Jadi, lanjutkan dan masukkan kembali koma, dan hapus komentar dua baris lainnya.

    Sekarang kita bisa pergi ke arah lain dengan memaksa sebuah collation. Daripada mengubah susunan bidang susunan koersible-default (yang sebagian besar dari mereka), kita harus dapat mengubah bidang susunan implisit ke bidang DB saat ini, kan?

    Jadi, sama seperti tes pertama kami, kami harus dapat memaksa pemeriksaan bidang 1 (RecoveryModel) dengan COLLATEklausa eksplisit . Tetapi jika kita menentukan susunan tertentu dan kemudian menjalankan kueri dalam database dengan susunan yang berbeda, bidang susunan koersible-default akan mengambil susunan baru yang kemudian akan bertentangan dengan apa yang kita setel pada kolom pertama ini. Sepertinya itu menyakitkan. Untungnya, ada cara dinamis untuk menghadapinya. Ada pseudo collation yang disebut DATABASE_DEFAULTyang mengambil collation basis data saat ini (seperti halnya bidang coercible-default).

    Teruskan dan tambahkan berikut ini di akhir baris, ke atas, yang dimulai dengan , RecoveryModel: COLLATE DATABASE_DEFAULT

    Jalankan kueri itu. Benar saja, kesalahannya menyatakan, sekali lagi, bahwa PageVerifybidang itulah yang memiliki konflik.

  4. Untuk tes akhir, kita tidak perlu membatalkan perubahan sebelumnya.

    Yang perlu kita lakukan sekarang untuk memperbaiki UNPIVOTkueri ini adalah menambahkan COLLATE DATABASE_DEFAULTke ujung bidang susunan implisit yang tersisa: PageVerifydan RestrictedAccess. Sementara Collationbidang juga merupakan susunan tersirat, bidang itu berasal dari masterbasis data, yang biasanya sejalan dengan basis data "saat ini". Tetapi, jika Anda ingin aman agar ini selalu berhasil, silakan tambahkan juga COLLATE DATABASE_DEFAULTpada akhir bidang itu.

    Jalankan kueri itu. Benar saja, tidak ada kesalahan. Semua yang diperlukan untuk memperbaiki kueri ini ditambahkan COLLATE DATABASE_DEFAULTke akhir 3 bidang (wajib) dan mungkin 1 lagi (opsional).

  5. Opsional Test: Sekarang kita akhirnya memiliki query UNPIVOT bekerja dengan benar, perubahan hanya satu dari setiap definisi lapangan dimulai dengan CONVERT(VARCHAR(50),bukan untuk menjadi 51, seperti dalam: CONVERT(VARCHAR(51),.

    Jalankan kueri. Anda harus mendapatkan The type of column "X" conflicts with the type of other columns specified in the UNPIVOT list.kesalahan yang sama dengan yang Anda dapatkan ketika hanya pemeriksaan yang tidak cocok.

    Mendapatkan kesalahan yang sama untuk ketidakcocokan datatype dan collation tidak cukup spesifik untuk benar-benar bermanfaat. Jadi pasti ada ruang untuk perbaikan di sana :).


Catatan terkait dengan Kueri lebih dari pada Pertanyaan spesifik tentang Collation:

Karena semua bidang sumber adalah tipe data NVARCHAR, itu akan lebih aman untuk CONVERTsemua bidang keluaran NVARCHARdaripada VARCHAR. Anda mungkin tidak sedang berurusan dengan data pada saat ini yang memiliki karakter non-standar-ASCII, tetapi sistem meta-data memungkinkan mereka untuk mengubahnya NVARCHAR(128)- yang merupakan panjang maksimum terbesar dari semua bidang tersebut - setidaknya menjamin bahwa tidak akan ada masalah bagi Anda di masa depan, atau bagi siapa pun yang menyalin kode ini yang mungkin sudah memiliki beberapa karakter tersebut di sistem mereka.

Solomon Rutzky
sumber
5

Ini adalah solusi untuk masalah khusus dan bukan jawaban lengkap untuk pertanyaan itu. Anda dapat menghindari kesalahan dengan mengkonversi ke sql_variantketimbang 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;

Saya telah menambahkan tiga kolom untuk informasi tentang jenis OptionValuekolom yang mendasarinya .

Output sampel

Jika klien tidak dapat menangani sql_variantdata, lakukan konversi akhir (tingkat atas) pada unpvt.OptionValuekolom, misalnya nvarchar(256).

Paul White mengatakan GoFundMonica
sumber
4

Ok, jadi saya melihat

sp_helptext [sys.databases]

kemudian memecah dari mana kolom itu berasal. Yang dengan Latin1_General_CI_AS_KS_WScollation semuanya berasal dari tabel sistem sys.syspalvaluesyang tampaknya merupakan tabel pencarian umum (ini adalah tabel sistem, jadi Anda harus terhubung melalui DAC untuk melihatnya.).

Dugaan saya adalah bahwa ia diatur Latin1_General_CI_AS_KS_WSuntuk menangani nilai-nilai pencarian yang mungkin. Saya bisa melihat bagaimana itu akan mengganggu.

Cara lain untuk melihat definisi (awalnya disediakan oleh Max dalam komentar) adalah:

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';`
Kenneth Fisher
sumber