Che succede con la raccolta di alcune colonne nei database sys.d?


21

Sto tentando di eseguire una UNPIVOTsu varie colonne contenute in sys.databasesvarie versioni di SQL Server, che vanno dal 2005 al 2012.

La UNPIVOTsta fallendo con il seguente messaggio di errore:

Messaggio 8167, livello 16, stato 1, linea 48

Il tipo di colonna "CompatibilityLevel" è in conflitto con il tipo di altre colonne specificato nell'elenco UNPIVOT.

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

Questo è progettato per fornire un elenco ben formattato di opzioni di database per il database dato, simile a:

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

Quando eseguo questo in un server con Latin1_General_CI_AS_KS_WSregole di confronto, la dichiarazione ha esito positivo. Se modifico il T-SQL in modo che alcuni campi abbiano una COLLATEclausola, verrà eseguito su server che hanno altre regole di confronto.

Il codice che funziona su server con regole di confronto diverse da 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;

Il comportamento osservato è che i seguenti campi non osservano né le regole di confronto del server né le regole di confronto del database; sono sempre presentati in Latin1_General_CI_AS_KS_WSconfronto.

Su SQL Server 2012, possiamo utilizzare sys.sp_describe_first_result_setper ottenere facilmente metadati relativi alle colonne restituite da una query specifica. Ho usato quanto segue per determinare la mancata corrispondenza delle regole di confronto:

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;

I risultati:

inserisci qui la descrizione dell'immagine

Perché le regole di confronto di queste colonne sono impostate staticamente?

Risposte:


17

La parola ufficiale di Microsoft:

Alcune delle colonne che contengono stringhe predefinite (come tipi, descrizioni di sistema e costanti) sono sempre fissate su regole di confronto specifiche Latin1_General_CI_AS_KS_WS. Ciò è indipendente dalle regole di confronto istanza / database. Il motivo è che si tratta di metadati di sistema (non metadati dell'utente) e sostanzialmente queste stringhe sono trattate senza distinzione tra maiuscole e minuscole (come le parole chiave, quindi sempre in latino).

Altre colonne nelle tabelle di sistema che contengono metadati utente come nomi di oggetti, nomi di colonne, nomi di indice, nomi di accesso, ecc. Accettano l'istanza o il confronto del database. Le colonne vengono fascicolate in modo appropriato al momento dell'installazione di SQL Server in caso di confronto delle istanze e al momento della creazione del database in caso di confronto del database.

Hai chiesto (enfasi alla mia):

Perché le regole di confronto di queste colonne sono impostate staticamente?

Il motivo per cui alcune colonne sono impostate in modo statico è che le query non devono preoccuparsi delle regole di confronto dei server o dei database (cosa più importante: SICUREZZA SICURA) per funzionare correttamente. Questa query funzionerà sempre indipendentemente dalle regole di confronto:

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

Considerando che se le regole di confronto del server fanno distinzione tra maiuscole e minuscole, la query sopra restituisce 0 righe, proprio come questo:

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

Ad esempio, se si installa un'istanza di SQL Server con SQL_Estonian_CP1257_CS_ASregole di confronto, eseguire quanto segue:

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

Vedrai questi risultati (o qualcosa di simile, a seconda della versione di 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

Ora, per dimostrare le visualizzazioni dei metadati che ereditano le regole di confronto del database, anziché ereditare le regole di confronto del server dal database principale:

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

risultati:

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

Quindi puoi vedere che in questo caso diverse colonne ereditano le regole di confronto del database, mentre altre sono fissate a queste regole di confronto "generiche" Latin1, il che significa che viene utilizzato per isolare determinati nomi e proprietà dai problemi di maiuscole e minuscole come descritto sopra.

Se si tenta di eseguire un UNION, ad esempio:

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

Ottieni questo errore:

Messaggio 451, livello 16, stato 1
Impossibile risolvere il conflitto di regole di confronto tra "Albanian_BIN" e "SQL_Estonian_CP1257_CS_AS" nell'operatore UNION ALL che si verificano nella colonna 1 dell'istruzione SELECT.

Allo stesso modo, se si tenta di eseguire un PIVOTo UNPIVOT, le regole sono ancora più rigorose (i tipi di output devono corrispondere tutti esattamente anziché essere semplicemente compatibili), ma il messaggio di errore è molto meno utile e persino fuorviante:

Messaggio 8167, livello 16, stato 1
Il tipo di colonna "nome colonna" è in conflitto con il tipo di altre colonne specificato nell'elenco UNPIVOT.

È necessario aggirare questi errori utilizzando COLLATEclausole esplicite nelle query. Ad esempio, l'unione sopra potrebbe essere:

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;

L'unica volta che ciò può causare problemi, otterrai un output confuso se una collation è forzata ma non contiene la stessa rappresentazione di carattere, o se viene utilizzato l'ordinamento e la fascicolazione forzata utilizza un ordinamento diverso rispetto alla sorgente.


7

Informazioni sulla precedenza delle regole di confronto

Il comportamento che si osserva in relazione alla raccolta di vari campi nelle viste del catalogo di sistema è il risultato della definizione di ciascun campo e della precedenza di confronto.

Quando si guarda sys.databases, è importante tenere presente che non è un tavolo. Mentre in passato (penso che finisca in SQL Server 2000) queste erano tabelle di catalogo di sistema , ora sono viste del catalogo di sistema . Quindi, la fonte delle informazioni in essi contenute non proviene necessariamente dal contesto del database corrente (o dal contesto del database specificato quando si tratta di un oggetto completo come master.sys.databases).

Trattare in particolare con sys.databases, alcuni dei campi provengono dal [master]database (che è stato creato con un confronto basato sulla raccolta predefinita dell'istanza - ovvero confronto a livello di server), alcuni dei campi sono espressioni (ovvero CASEistruzioni), e alcuni stanno arrivando da una fonte "nascosta": il [mssqlsystemresource]database. E il [mssqlsystemresource]database ha un confronto di: Latin1_General_CI_AS_KS_WS.

Il namecampo proviene dal namecampo in master.sys.sysdbreg. Quindi questo campo dovrebbe sempre trovarsi nelle regole di confronto del [master]database, che corrisponderà nuovamente alle regole di confronto del server.

MA, i seguenti campi sys.databasesprovengono dal [name]campo in [mssqlsystemresource].[sys].[syspalvalues]:

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

Quei campi dovrebbero sempre avere un confronto di Latin1_General_CI_AS_KS_WS.

Il collation_namecampo, tuttavia, deriva dalla seguente espressione:

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)

È qui che inizia la Precedenza di confronto . Entrambe le opzioni per l'output qui sono funzioni di sistema: serverproperty()e collationpropertyfromid(). Le regole di confronto di questa espressione sono considerate "Coercible-default":

Qualsiasi variabile di stringa di carattere Transact-SQL, parametro, valore letterale o output di una funzione incorporata del catalogo o di una funzione incorporata che non accetta input di stringa ma produce un output di stringa.

Se l'oggetto viene dichiarato in una funzione definita dall'utente, in una procedura memorizzata o in un trigger, all'oggetto viene assegnato il confronto predefinito del database in cui viene creata la funzione, la procedura memorizzata o il trigger. Se l'oggetto viene dichiarato in un batch, all'oggetto viene assegnato il confronto predefinito del database corrente per la connessione.

Alla luce di quel secondo paragrafo, poiché sys.databasesesiste una vista presente nel masterdatabase, assume le regole di confronto del masterdatabase (non il database corrente).

Il state_desccampo è anche un'espressione:

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

Ma la collazione su questa espressione è Latin1_General_CI_AS_KS_WS. Perché? Bene, qualcosa di nuovo viene introdotto in questa espressione: un riferimento a un campo reale: [mssqlsystemresource].[sys].[syspalvalues].[name]in quella ELSEclausola finale . I riferimenti alle colonne sono considerati "impliciti":

Un riferimento di colonna. Le regole di confronto dell'espressione sono prese dalle regole di confronto definite per la colonna nella tabella o nella vista.

Naturalmente, questo apre una domanda interessante: è possibile che questa espressione restituisca una diversa confronto a seconda di come CASEviene valutata? I valori letterali si troveranno nelle regole di confronto del database in cui è definito questo oggetto, ma la ELSEcondizione restituisce un valore di campo che dovrebbe conservare le regole di confronto originali. Fortunatamente, possiamo simulare un test utilizzando la funzione di gestione dinamica 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

Restituisce (su un'istanza impostata con regole di confronto SQL_Latin1_General_CP1_CI_ASma in esecuzione in un database con regole di confronto di 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

Qui vediamo che le due query che fanno riferimento al campo [msdb]prendono le regole di confronto del [msdb]database (che, essendo un DB di sistema, è stato determinato dalle regole di confronto del server).

Relativo alla domanda originale

Il comportamento osservato è che i seguenti campi non osservano né le regole di confronto del server né le regole di confronto del database; sono sempre presentati in Latin1_General_CI_AS_KS_WSconfronto.

La tua osservazione è precisa: quei campi hanno sempre un confronto Latin1_General_CI_AS_KS_WS, indipendentemente dal confronto tra server o database. E la ragione è: la precedenza sulla collazione. Tali campi provengono da una tabella nel [mssqlsystemresource]database e manterranno le regole di confronto iniziali a meno che non vengano sovrascritte con una COLLATEclausola esplicita poiché questa ha la precedenza più alta:

Explicit = Un'espressione che viene esplicitamente lanciata in un confronto specifico utilizzando una clausola COLLATE nell'espressione.

L'esplicito ha la precedenza sull'implicito. Implicito ha la precedenza sul valore predefinito Coercibile:
esplicito> Implicito> valore predefinito Coercibile

E la domanda correlata:

Perché le regole di confronto di queste colonne sono impostate staticamente?

Non è che siano impostati staticamente, né che gli altri campi siano in qualche modo dinamici. Tutti i campi in tutte quelle viste del catalogo di sistema funzionano con le stesse regole di Precedenza regole di confronto. Il motivo per cui sembrano essere più "statici" rispetto agli altri campi (ovvero non cambiano anche se si installa SQL Server con un confronto predefinito diverso, che a sua volta crea i database di sistema con tale confronto predefinito) è che il [mssqlsystemresource]database è coerente ha un confronto Latin1_General_CI_AS_KS_WStra tutte le installazioni di SQL Server (o almeno così appare). E questo ha senso perché altrimenti sarebbe difficile per SQL Server gestirsi internamente (ovvero se le regole di ordinamento e confronto utilizzate per la logica interna cambiassero in base all'installazione).

Come vedere questi dettagli da soli

Se vuoi vedere l'origine di uno o più campi in una di queste viste del catalogo di sistema, procedi come segue:

  1. In una scheda query in SSMS, abilita l'opzione Query di "Includi piano di esecuzione effettivo" ( CTRL-M)
  2. Esegui una query selezionando un campo da una delle viste del catalogo di sistema (ti consiglio di selezionare solo un campo alla volta poiché il piano di esecuzione è ridicolmente grande / complesso anche per un solo campo e includerà riferimenti a molti campi che non sono " t selezionando):

    SELECT recovery_model_desc FROM sys.databases;
  3. Vai alla scheda "Piano di esecuzione"
  4. Fare clic con il tasto destro nell'area grafica del piano di esecuzione e selezionare "Mostra XML piano di esecuzione ..."
  5. Si aprirà una nuova scheda in SSMS con un titolo simile a: Execution plan.xml
  6. Vai alla Execution plan.xmlscheda
  7. Cerca la prima occorrenza di un <OutputList>tag (dovrebbe essere tra le righe 10 e 20 in genere)
  8. Dovrebbe esserci un <ColumnReference>tag. Gli attributi in quel tag devono puntare a un campo specifico in una tabella oppure a un'espressione definita più avanti nel piano.
  9. Se gli attributi puntano a un campo reale, allora hai finito in quanto ha tutte le informazioni. Quello che segue è ciò che appare per il recovery_model_desccampo:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
  10. Se gli attributi puntano a un'espressione, ad esempio se invece hai selezionato il state_desccampo, inizialmente troverai:

    <ColumnReference Column="Expr1024" />
  11. In questo caso, è necessario esaminare il resto del piano per la definizione Expr1024o qualunque # venga fornito. Tieni presente che potrebbero esserci diversi di questi riferimenti, ma la definizione non sarà in un <OutputList>blocco. Tuttavia, avrà un <ScalarOperator>elemento fratello che contiene la definizione. Quello che segue è ciò che appare per il state_desccampo:

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

Lo stesso può essere fatto per controllare anche l'origine delle viste del catalogo a livello di database. Fare questo per un oggetto simile sys.tablesmostrerà che il namecampo proviene [current_db].[sys].[sysschobjs](motivo per cui ha un confronto che corrisponde al confronto del database), mentre il lock_escalation_desccampo proviene [mssqlsystemresource].[sys].[syspalvalues](motivo per cui ha un confronto di Latin1_General_CI_AS_KS_WS).

Clippy dice "Sembra che tu voglia fare una query UNPIVOT."

Ora che sappiamo perché cos'è la precedenza sulle regole di confronto e come funziona, applichiamo tale conoscenza alla query UNPIVOT.

Per UNPIVOTun'operazione, SQL Server sembra davvero preferire (significato: richiede) che ogni campo di origine sia dello stesso tipo esatto . Di solito "tipo" si riferisce al tipo di base (ovvero VARCHAR/ NVARCHAR/ INT/ etc) ma qui SQL Server include anche la COLLATION. Questo non dovrebbe essere visto come irragionevole dato ciò che controlla le regole di confronto: il set di caratteri (ad es. Pagina di codice) per VARCHAR e le regole linguistiche che determinano l'equivalenza dei caratteri e le combinazioni di caratteri (ad esempio la normalizzazione). Quello che segue è un mimi-primer su cosa sia la "normalizzazione" 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 '---';

Ritorna:

---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---

Quindi ora iniziamo la query originale. Faremo alcuni test per vedere come varie modifiche alterano il risultato, e poi vedremo come solo poche modifiche possono risolverlo.

  1. Il primo errore riguarda il CompatibilityLevel campo, che è il secondo campo a non essere pivotato, e sembra essere un'espressione contenente tutti i letterali di stringa. Senza riferimenti sul campo, le regole di confronto risultanti sono considerate "valore predefinito". I valori predefiniti coercibili assumono le regole di confronto del database corrente, diciamo SQL_Latin1_General_CP1_CI_AS. I prossimi 20 o giù di lì sono anche solo letterali stringa e quindi sono anche valori predefiniti coercibili, quindi non dovrebbero essere in conflitto. Ma se guardiamo indietro al primo campo, recovery_model_descche proviene direttamente da un campo in sys.databases, il che lo rende un confronto "implicito", e questo non assume le regole di confronto del DB locale, ma conserva invece le regole di confronto originali, che è Latin1_General_CI_AS_KS_WS( perché proviene davvero dal [mssqlsystemresource]DB).

    Quindi, se il campo 1 (RecoveryModel) è Latin1_General_CI_AS_KS_WS, e il campo 2 (CompatibilityLevel) lo è SQL_Latin1_General_CP1_CI_AS, allora dovremmo essere in grado di forzare il campo 2 affinché corrisponda Latin1_General_CI_AS_KS_WSal campo 1, e quindi l'errore dovrebbe apparire per il campo 3 (Chiusura automatica).

    Aggiungi quanto segue alla fine della CompatibilityLevelriga:
    COLLATE Latin1_General_CI_AS_KS_WS

    E quindi eseguire la query. Abbastanza sicuro, l'errore ora afferma che è il AutoClosecampo che ha il conflitto.

  2. Per il nostro secondo test abbiamo bisogno di annullare la modifica che abbiamo appena fatto (cioè rimuovere il file COLLATE clausola dalla fine della CompatibilityLevelriga.

    Ora, se SQL Server sta veramente valutando nell'ordine in cui sono specificati i campi, dovremmo essere in grado di rimuovere il campo 1 (RecoveryModel), che farà sì che l'attuale campo 2 (CompatibilityLevel) sia il campo che imposta le regole di confronto principali di l'UNPIVOT risultante. E ilCompatibilityLevel campo è un valore predefinito coercibile che assume le regole di confronto del database, quindi il primo errore dovrebbe essere il PageVerifycampo, che è un riferimento di campo, che è una raccolta implicita che mantiene le regole di confronto originali, che in questo caso è Latin1_General_CI_AS_KS_WSe che non è il regole di confronto del DB corrente.

    Quindi, andare avanti e commentare la riga che inizia con , RecoveryModella SELECT(verso l'alto) e poi commentare la RecoveryModellinea delUNPIVOT clausola di seguito e rimuovere la virgola che conduce dal seguente riga per CompatibilityLevelmodo che non si ottiene un errore di sintassi.

    Esegui quella query. Abbastanza sicuro, l'errore ora afferma che è il PageVerifycampo che ha il conflitto.

  3. Per il nostro terzo test, dobbiamo annullare le modifiche che abbiamo appena fatto per rimuovere il file RecoveryModel campo. Quindi vai avanti e rimetti la virgola e rimuovi il commento da quelle altre due righe.

    Ora possiamo andare nella direzione opposta forzando una collazione. Invece di cambiare le regole di confronto dei campi di regole predefinite coercibili (che rappresentano la maggior parte di esse), dovremmo essere in grado di modificare i campi di confronto impliciti con quelli del DB corrente, giusto?

    Quindi, proprio come il nostro primo test, dovremmo essere in grado di forzare il confronto del campo 1 (RecoveryModel) con una COLLATEclausola esplicita . Ma se specifichiamo un particolare confronto e quindi eseguiamo la query in un database con un diverso confronto, i campi di confronto predefiniti coercibili raccoglieranno il nuovo confronto che entrerà quindi in conflitto con ciò su cui stiamo impostando questo primo campo. Sembra un dolore. Fortunatamente, esiste un modo dinamico per affrontarlo. Esiste una pseudo fascicolazione chiamata DATABASE_DEFAULTche raccoglie le regole di confronto dei database correnti (proprio come fanno i campi predefiniti coercibili).

    Vai avanti e aggiungi quanto segue alla fine della linea, verso l'alto, che inizia con , RecoveryModel: COLLATE DATABASE_DEFAULT

    Esegui quella query. Abbastanza sicuro, l'errore afferma, ancora una volta, che è il PageVerifycampo che ha il conflitto.

  4. Per il test finale, non è necessario annullare nessuna delle modifiche precedenti.

    Tutto ciò che dobbiamo fare ora per correggere questa UNPIVOTquery è aggiungere COLLATE DATABASE_DEFAULTla fine dei rimanenti campi di confronto impliciti: PageVerifye RestrictedAccess. Sebbene il Collationcampo sia anche un confronto implicito, quel campo proviene dal masterdatabase, che in genere è in linea con il database "corrente". Ma, se vuoi essere sicuro in modo che COLLATE DATABASE_DEFAULTfunzioni sempre, allora vai avanti e aggiungi anche la fine di quel campo.

    Esegui quella query. Abbastanza sicuro, nessun errore. Tutto ciò che è stato necessario per risolvere questa query è stato l'aggiunta COLLATE DATABASE_DEFAULTalla fine di 3 campi (obbligatorio) e forse 1 altro (facoltativo).

  5. Test facoltativo: ora che finalmente la query UNPIVOT funziona correttamente, cambia solo una delle definizioni dei campi che iniziano con CONVERT(VARCHAR(50),essere invece 51, come in:CONVERT(VARCHAR(51), .

    Esegui la query. Dovresti ottenere lo stesso The type of column "X" conflicts with the type of other columns specified in the UNPIVOT list.errore che hai avuto quando si trattava solo delle regole di confronto che non corrispondevano.

    Ottenere lo stesso errore sia per il tipo di dati che per i disallineamenti delle regole di confronto non è abbastanza specifico per essere veramente utile. Quindi c'è sicuramente spazio per miglioramenti lì :).


Nota relativa alla query più che alla domanda specifica sulla raccolta:

Dal momento che tutti i campi di origine sono del tipo di dati NVARCHAR, sarebbe più sicuro per CONVERTtutti i campi di output al NVARCHARposto di VARCHAR. Al momento potresti non avere a che fare con dati contenenti caratteri ASCII non standard, ma i metadati di sistema li consentono di convertirli in NVARCHAR(128)- che è la massima lunghezza massima di uno di questi campi - almeno garantisce che non ci saranno problemi per te in futuro o per chiunque copi questo codice che potrebbe già avere alcuni di quei caratteri nel proprio sistema.


5

Questa è una soluzione alternativa per il problema specifico piuttosto che una risposta completa alla domanda. È possibile evitare l'errore convertendo in sql_variantanziché anziché 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;

Ho aggiunto tre colonne per informazioni sul tipo di OptionValuecolonna sottostante .

Uscita campione

Se il client non è in grado di gestire i sql_variantdati, eseguire una conversione finale (livello superiore) sulla unpvt.OptionValuecolonna, ad es nvarchar(256).


4

Ok, quindi ho dato un'occhiata

sp_helptext [sys.databases]

poi si è rotto da dove provenivano le colonne. Quelli con le Latin1_General_CI_AS_KS_WSregole di confronto provengono tutti dalla tabella di sistemasys.syspalvalues che sembra essere una tabella di ricerca generica (è una tabella di sistema, quindi dovrai collegarti tramite il DAC per vederlo.).

La mia ipotesi è che è impostato su Latin1_General_CI_AS_KS_WS per gestire eventuali valori di ricerca. Posso vedere come sarebbe fastidioso però.

Un altro modo per vedere la definizione (originariamente fornita da Max in un commento) è:

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';`
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.