it-swarm.com.de

Was ist mit der Zusammenstellung einiger Spalten in sys.databases los?

Ich versuche, ein UNPIVOT für verschiedene in sys.databases Enthaltene Spalten in verschiedenen Versionen von SQL Server von 2005 bis 2012 auszuführen.

Das UNPIVOT schlägt mit der folgenden Fehlermeldung fehl:

Nachricht 8167, Ebene 16, Status 1, Zeile 48

Der Spaltentyp "CompatibilityLevel" steht in Konflikt mit dem Typ anderer Spalten, die in der UNPIVOT-Liste angegeben sind.

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

Dies soll eine gut formatierte Liste von Datenbankoptionen für die angegebene Datenbank bereitstellen, ähnlich wie:

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

Wenn ich dies auf einem Server mit Latin1_General_CI_AS_KS_WS - Sortierung ausführe, ist die Anweisung erfolgreich. Wenn ich T-SQL so ändere, dass bestimmte Felder eine COLLATE -Klausel enthalten, wird es auf Servern mit anderen Sortierungen ausgeführt.

Der Code, der auf Servern mit anderen Kollatierungen als Latin1_General_CI_AS_KS_WS Funktioniert, lautet:

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;

Das beobachtete Verhalten ist, dass die folgenden Felder weder die Server-Sortierung noch die Datenbank-Sortierung beobachten. Sie werden immer in der Kollatierung Latin1_General_CI_AS_KS_WS dargestellt.

Unter SQL Server 2012 können wir sys.sp_describe_first_result_set Verwenden, um auf einfache Weise Metadaten zu den von einer bestimmten Abfrage zurückgegebenen Spalten abzurufen. Ich habe Folgendes verwendet, um die Kollatierungsfehlanpassung zu bestimmen:

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;

Die Ergebnisse:

(enter image description here

Warum wird die Sortierung dieser Spalten statisch festgelegt?

21
Max Vernon

Das offizielle Wort von Microsoft:

Einige der Spalten, die vordefinierte Zeichenfolgen enthalten (wie Typen, Systembeschreibungen und Konstanten), sind immer auf eine bestimmte Sortierung festgelegt - Latin1_General_CI_AS_KS_WS. Dies ist unabhängig von der Instanz-/Datenbankkollatierung. Der Grund dafür ist, dass dies Systemmetadaten sind (keine Benutzermetadaten) und diese Zeichenfolgen grundsätzlich ohne Berücksichtigung der Groß- und Kleinschreibung behandelt werden (wie Schlüsselwörter, also immer lateinisch).

Andere Spalten in Systemtabellen, die Benutzermetadaten wie Objektnamen, Spaltennamen, Indexnamen, Anmeldenamen usw. enthalten, übernehmen die Instanz- oder Datenbankkollatierung. Die Spalten werden zum Zeitpunkt der Installation von SQL Server im Falle einer Instanzsortierung und zum Zeitpunkt der Erstellung der Datenbank im Falle einer Datenbanksortierung auf die richtige Sortierung sortiert.

Sie haben gefragt (Hervorhebung von mir):

Warum ist die Sortierung dieser Spalten statisch festgelegt?

Der Grund, warum einige Spalten statisch festgelegt sind, besteht darin, dass Abfragen sich nicht um die Server- oder Datenbankkollatierung kümmern müssen (was noch wichtiger ist: CaSe SenSiTIviTy), um ordnungsgemäß zu funktionieren. Diese Abfrage funktioniert immer unabhängig von der Sortierung:

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

Wenn bei der Server-Sortierung zwischen Groß- und Kleinschreibung unterschieden würde, würde die obige Abfrage 0 Zeilen zurückgeben, genau wie folgt:

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

Wenn Sie beispielsweise eine Instanz von SQL Server mit der Kollatierung SQL_Estonian_CP1257_CS_AS Installieren, führen Sie Folgendes aus:

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

Sie sehen diese Ergebnisse (oder etwas Ähnliches, abhängig von Ihrer Version von 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

Um nun Metadatenansichten zu demonstrieren, die die Datenbankkollatierung erben, anstatt die Serverkollatierung von der Masterdatenbank zu erben:

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

Ergebnisse:

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

Sie können also sehen, dass in diesem Fall mehrere Spalten die Datenbankkollatierung erben, während andere auf diese "generische" Latin1-Kollatierung festgelegt sind. Dies bedeutet, dass bestimmte Namen und Eigenschaften wie oben beschrieben von Problemen mit der Groß- und Kleinschreibung isoliert werden.

Wenn Sie versuchen, ein UNION auszuführen, zum Beispiel:

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

Sie erhalten diesen Fehler:

Nachricht 451, Ebene 16, Status 1
Kollatierungskonflikt zwischen "Albanian_BIN" und "SQL_Estonian_CP1257_CS_AS" im UNION ALL-Operator in Spalte 1 der SELECT-Anweisung kann nicht gelöst werden.

Wenn Sie versuchen, ein PIVOT oder UNPIVOT auszuführen, sind die Regeln noch strenger (die Ausgabetypen müssen alle mit genau übereinstimmen, anstatt nur kompatibel zu sein.) , aber die Fehlermeldung ist weit weniger hilfreich und sogar irreführend:

Nachricht 8167, Ebene 16, Status 1
Der Typ der Spalte "Spaltenname" steht in Konflikt mit dem Typ anderer Spalten, die in der UNPIVOT-Liste angegeben sind.

Sie müssen diese Fehler mit expliziten COLLATE -Klauseln in Ihren Abfragen umgehen. Zum Beispiel könnte die obige Vereinigung sein:

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;

Dies kann nur dann zu Problemen führen, wenn eine Kollatierung erzwungen wird, aber nicht dieselbe Zeichendarstellung enthält oder wenn die Sortierung verwendet wird und die erzwungene Kollatierung eine andere Sortierreihenfolge als die Quelle verwendet.

17
Aaron Bertrand

Hintergrundinformationen zur Rangfolge

Das Verhalten, das Sie in Bezug auf die Sortierung verschiedener Felder in den Systemkatalogansichten sehen, hängt davon ab, wie die einzelnen Felder definiert sind und welche Rangfolge sie haben.

Wenn Sie sich sys.databases Betrachten, ist es wichtig zu beachten, dass es sich nicht um eine Tabelle handelt. Während in der Vergangenheit (ich glaube, mit SQL Server 2000 endend) dies Systemkatalog Tabellen waren, sind sie jetzt Systemkatalog Ansichten. Daher stammt die Quelle der darin enthaltenen Informationen nicht unbedingt aus dem aktuellen Datenbankkontext (oder dem Kontext der angegebenen Datenbank, wenn es sich um ein vollständig qualifiziertes Objekt wie master.sys.databases Handelt).

Einige der Felder, die sich speziell mit sys.databases Befassen, stammen aus der Datenbank [master] (Die mit einer Kollatierung erstellt wurde, die auf der Standardkollatierung der Instanz basiert - dh der Kollatierung auf Serverebene) Felder sind Ausdrücke (dh CASE Anweisungen), und einige stammen aus einer "versteckten" Quelle: der [mssqlsystemresource] - Datenbank. Und die [mssqlsystemresource] - Datenbank hat eine Zusammenstellung von: Latin1_General_CI_AS_KS_WS.

Das Feld name stammt aus dem Feld name in master.sys.sysdbreg. Dieses Feld sollte sich also immer in der Sortierung der Datenbank [master] Befinden, die wiederum mit der Sortierung des Servers übereinstimmt.

ABER die folgenden Felder in sys.databases Stammen aus dem Feld [name] In [mssqlsystemresource].[sys].[syspalvalues]:

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

Diese Felder sollten immer eine Sortierung von Latin1_General_CI_AS_KS_WS Haben.

Das Feld collation_name Stammt jedoch aus dem folgenden Ausdruck:

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)

Hier kommt Collation Precedence ins Spiel. Beide Optionen für die Ausgabe hier sind Systemfunktionen: serverproperty() und collationpropertyfromid(). Die Zusammenstellung dieses Ausdrucks wird als "Coercible-default" betrachtet:

Jede Transact-SQL-Zeichenfolgenvariable, jeder Parameter, jedes Literal oder die Ausgabe einer integrierten Katalogfunktion oder einer integrierten Funktion, die keine Zeichenfolgeneingaben akzeptiert, sondern eine Zeichenfolgenausgabe erzeugt.

Wenn das Objekt in einer benutzerdefinierten Funktion, gespeicherten Prozedur oder einem Trigger deklariert ist, wird dem Objekt die Standardkollatierung der Datenbank zugewiesen, in der die Funktion, die gespeicherte Prozedur oder der Trigger erstellt werden. Wenn das Objekt in einem Stapel deklariert ist, wird dem Objekt die Standardkollatierung der aktuellen Datenbank für die Verbindung zugewiesen.

In Anbetracht dieses zweiten Absatzes übernimmt sys.databases, Da es sich um eine Ansicht handelt, die in der Datenbank master vorhanden ist, die Sortierung der Datenbank master (nicht der aktuellen Datenbank).

Das Feld state_desc Ist auch ein Ausdruck:

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

Die Sortierung für diesen Ausdruck lautet jedoch Latin1_General_CI_AS_KS_WS. Warum? Nun, in diesem Ausdruck wird etwas Neues eingeführt: ein Verweis auf ein reales Feld: [mssqlsystemresource].[sys].[syspalvalues].[name] In dieser letzten ELSE -Klausel. Spaltenreferenzen gelten als "implizit":

Eine Spaltenreferenz. Die Sortierung des Ausdrucks wird der für die Spalte in der Tabelle oder Ansicht definierten Sortierung entnommen.

Dies wirft natürlich eine interessante Frage auf: Kann dieser Ausdruck eine andere Sortierung zurückgeben, je nachdem, wie das CASE ausgewertet wird? Die Literale befinden sich in der Sortierung der Datenbank, in der dieses Objekt definiert ist, aber die Bedingung ELSE gibt einen Feldwert zurück, der seine ursprüngliche Sortierung beibehalten soll. Glücklicherweise können wir einen Test mit der Funktion sys.dm_exec_describe_first_result_set Dynamic Management simulieren:

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

Rückgabe (auf einer Instanz, die mit einer Sortierung von SQL_Latin1_General_CP1_CI_AS Einrichtet, aber in einer Datenbank mit einer Sortierung von Japanese_Unicode_CI_AS Ausgeführt wird):

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

Hier sehen wir, dass die beiden Abfragen, die auf das Feld in [msdb] Verweisen, die Sortierung der Datenbank [msdb] Übernehmen (die als System-DB durch die Server-Sortierung bestimmt wurde).

Zurück zur ursprünglichen Frage

Das beobachtete Verhalten ist, dass die folgenden Felder weder die Server-Sortierung noch die Datenbank-Sortierung beobachten. Sie werden immer in der Kollatierung Latin1_General_CI_AS_KS_WS dargestellt.

Ihre Beobachtung ist genau richtig: Diese Felder haben immer eine Sortierung von Latin1_General_CI_AS_KS_WS, Unabhängig von der Server- oder Datenbankkollatierung. Und der Grund ist: Kollatierungsrang. Diese Felder stammen aus einer Tabelle in der Datenbank [mssqlsystemresource] Und behalten diese anfängliche Sortierung bei, sofern sie nicht mit einer expliziten COLLATE -Klausel überschrieben werden, da diese die höchste Priorität hat:

Explizit = Ein Ausdruck, der mithilfe einer COLLATE-Klausel im Ausdruck explizit in eine bestimmte Kollatierung umgewandelt wird.

Explizit hat Vorrang vor implizit. Implizit hat Vorrang vor Coercible-Standard:
Explizit> Implizit> Zwangsvorgabe

Und die damit verbundene Frage:

Warum wird die Sortierung dieser Spalten statisch festgelegt?

Es ist nicht so, dass sie statisch festgelegt sind oder dass die anderen Felder irgendwie dynamisch sind. Alle Felder in all diesen Systemkatalogansichten arbeiten nach denselben Regeln der Kollatierungsrangfolge. Der Grund dafür, dass sie "statischer" zu sein scheinen als die anderen Felder (dh sie ändern sich nicht, selbst wenn Sie SQL Server mit einer anderen Standardkollatierung installieren, wodurch wiederum die Systemdatenbanken mit dieser Standardkollatierung erstellt werden), ist, dass [mssqlsystemresource] Datenbank hat konsistent eine Sortierung von Latin1_General_CI_AS_KS_WS In jeder Installation von SQL Server (oder so scheint es sicherlich). Dies ist sinnvoll, da es sonst für SQL Server schwierig wäre, sich intern zu verwalten (d. H. Wenn sich die für die interne Logik verwendeten Sortier- und Vergleichsregeln je nach Installation ändern).

Wie Sie diese Besonderheiten selbst sehen können

Wenn Sie die Quelle eines Feldes in einer dieser Systemkatalogansichten anzeigen möchten, gehen Sie wie folgt vor:

  1. Aktivieren Sie auf einer Registerkarte "Abfrage" in SSMS die Abfrageoption "Aktuellen Ausführungsplan einschließen" (CTRL-M)
  2. Führen Sie eine Abfrage aus, indem Sie ein Feld aus einer der Systemkatalogansichten auswählen (ich empfehle, jeweils nur ein Feld auszuwählen, da der Ausführungsplan selbst für ein einzelnes Feld lächerlich groß/komplex ist und Verweise auf viele Felder enthält, die Sie nicht sind. t auswählen):

    SELECT recovery_model_desc FROM sys.databases;
    
  3. Gehen Sie zur Registerkarte "Ausführungsplan"
  4. Klicken Sie mit der rechten Maustaste in den grafischen Bereich des Ausführungsplans und wählen Sie "Ausführungsplan-XML anzeigen ...".
  5. Eine neue Registerkarte in SSMS wird mit einem ähnlichen Titel geöffnet: Execution plan.xml
  6. Gehen Sie zur Registerkarte Execution plan.xml
  7. Suchen Sie nach dem ersten Auftreten eines <OutputList> - Tags (es sollte normalerweise zwischen den Zeilen 10 und 20 liegen).
  8. Es sollte ein <ColumnReference> - Tag geben. Die Attribute in diesem Tag sollten entweder auf ein bestimmtes Feld in einer Tabelle verweisen oder auf einen Ausdruck, der später im Plan definiert wird.
  9. Wenn die Attribute auf ein reales Feld verweisen, sind Sie fertig, da es alle Informationen enthält. Für das Feld recovery_model_desc Wird Folgendes angezeigt:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
    
  10. Wenn die Attribute auf einen Ausdruck verweisen, z. B. wenn Sie stattdessen das Feld state_desc Ausgewählt haben, finden Sie zunächst:

    <ColumnReference Column="Expr1024" />
    
  11. In diesem Fall müssen Sie den Rest des Plans nach der Definition von Expr1024 Oder einer beliebigen # durchsuchen. Denken Sie daran, dass es mehrere dieser Referenzen geben kann, die Definition jedoch nicht in einem <OutputList> - Block enthalten ist. Es wird jedoch ein Geschwisterelement <ScalarOperator> Haben, das die Definition enthält. Für das Feld state_desc Wird Folgendes angezeigt:

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

Auf die gleiche Weise können Sie auch die Quelle der Katalogansichten auf Datenbankebene überprüfen. Wenn Sie dies für ein Objekt wie sys.tables Tun, wird angezeigt, dass das Feld name von [current_db].[sys].[sysschobjs] Stammt (weshalb die Sortierung mit der Sortierung der Datenbank übereinstimmt), während das Feld lock_escalation_desc Stammt von [mssqlsystemresource].[sys].[syspalvalues] (Aus diesem Grund hat es eine Sortierung von Latin1_General_CI_AS_KS_WS).

Clippy sagt: "Es sieht so aus, als ob Sie eine UNPIVOT-Abfrage durchführen möchten."

Nachdem wir nun wissen, warum Collation Precedence ist und wie es funktioniert, wenden wir dieses Wissen auf die UNPIVOT-Abfrage an.

Für eine UNPIVOT -Operation scheint SQL Server wirklich zu bevorzugen (was bedeutet: erfordert), dass jedes Quellfeld vom Typ genau der gleiche Typ ist. Normalerweise bezieht sich "Typ" auf den Basistyp (d. H. VARCHAR/NVARCHAR/INT/etc), aber hier enthält SQL Server auch die COLLATION. Dies sollte nicht als unangemessen angesehen werden, wenn man bedenkt, was Kollatierungen steuern: den Zeichensatz (d. H. Die Codepage) für VARCHAR und die Sprachregeln, die die Äquivalenz von Zeichen und Zeichenkombinationen bestimmen (d. H. Normalisierung). Das Folgende ist ein Mimi-Primer für die Unicode- "Normalisierung":

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

Kehrt zurück:

---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---

Beginnen wir nun mit Ihrer ursprünglichen Abfrage. Wir werden einige Tests durchführen, um zu sehen, wie verschiedene Änderungen das Ergebnis verändern, und dann werden wir sehen, wie nur wenige Änderungen es beheben können.

  1. Der erste Fehler betrifft das Feld CompatibilityLevel, das das zweite Feld ist, das nicht gedreht wird, und ist zufällig ein Ausdruck, der alle Zeichenfolgenliterale enthält. Ohne Feldreferenzen wird die resultierende Sortierung als "Zwangsausfall" betrachtet. Coercible-Defaults übernehmen die Sortierung der aktuellen Datenbank, sagen wir SQL_Latin1_General_CP1_CI_AS. Die nächsten 20 Felder sind ebenfalls nur Zeichenfolgenliterale und daher auch Standardwerte, sodass sie nicht in Konflikt geraten sollten. Wenn wir jedoch auf das erste Feld zurückblicken, recovery_model_desc, Das direkt aus einem Feld in sys.databases Stammt, was es zu einer "impliziten" Kollation macht, und dies tut nicht Übernehmen Sie die Sortierung der lokalen Datenbank, behalten Sie jedoch die ursprüngliche Sortierung bei, die Latin1_General_CI_AS_KS_WS lautet (da sie wirklich aus der DB [mssqlsystemresource] stammt).

    Wenn also Feld 1 (RecoveryModel) Latin1_General_CI_AS_KS_WS Und Feld 2 (CompatibilityLevel) SQL_Latin1_General_CP1_CI_AS Ist, sollten wir in der Lage sein, Feld 2 zu zwingen, Latin1_General_CI_AS_KS_WS Mit dem Feld übereinzustimmen 1, und dann sollte der Fehler für Feld 3 (AutoClose) auftreten.

    Fügen Sie am Ende der Zeile CompatibilityLevel Folgendes hinzu:
    COLLATE Latin1_General_CI_AS_KS_WS

    Führen Sie dann die Abfrage aus. Sicher genug, der Fehler besagt jetzt, dass es das Feld AutoClose ist, das den Konflikt hat.

  2. Für unseren zweiten Test müssen wir die gerade vorgenommene Änderung rückgängig machen (d. H. Die COLLATE -Klausel am Ende der Zeile CompatibilityLevel entfernen.

    Wenn SQL Server nun wirklich in der Reihenfolge auswertet, in der die Felder angegeben sind, sollten wir in der Lage sein, Feld 1 (RecoveryModel) zu entfernen, wodurch das aktuelle Feld 2 (CompatibilityLevel) das Feld ist, für das die Master-Sortierung festgelegt wird das resultierende UNPIVOT. Und das Feld CompatibilityLevel ist ein Zwangsstandard, der die Datenbankkollatierung übernimmt. Der erste Fehler sollte also das Feld PageVerify sein, bei dem es sich um eine Feldreferenz handelt, bei der es sich um eine implizite Kollatierung handelt, bei der das Original beibehalten wird Kollatierung, die in diesem Fall Latin1_General_CI_AS_KS_WS ist und nicht die Kollatierung der aktuellen Datenbank ist.

    Kommentieren Sie also die Zeile aus, die mit , RecoveryModel In der SELECT (nach oben) beginnt, und kommentieren Sie dann die Zeile RecoveryModel in der Klausel UNPIVOT aus und entfernen Sie das führende Komma aus der folgenden Zeile für CompatibilityLevel, damit Sie keinen Syntaxfehler erhalten.

    Führen Sie diese Abfrage aus. Sicher genug, der Fehler besagt jetzt, dass es das Feld PageVerify ist, das den Konflikt hat.

  3. Für unseren dritten Test müssen wir die Änderungen rückgängig machen, die wir gerade vorgenommen haben, um das Feld RecoveryModel zu entfernen. Setzen Sie also das Komma zurück und kommentieren Sie die beiden anderen Zeilen aus.

    Jetzt können wir mit dem Erzwingen einer Kollatierung in die andere Richtung gehen. Anstatt die Kollatierung der Kollatierungsfelder mit dem erzwungenen Standard zu ändern (was die meisten davon sind), sollten wir in der Lage sein, die impliziten Kollatierungsfelder in die der aktuellen Datenbank zu ändern, oder?

    Ähnlich wie bei unserem ersten Test sollten wir in der Lage sein, die Sortierung von Feld 1 (RecoveryModel) mit einer expliziten COLLATE -Klausel zu erzwingen. Wenn wir jedoch eine bestimmte Kollatierung angeben und dann die Abfrage in einer Datenbank mit einer anderen Kollatierung ausführen, nehmen die Felder für die Standardkollatierung die neue Kollatierung auf, die dann in Konflikt mit dem steht, auf das wir dieses erste Feld setzen. Das scheint ein Schmerz zu sein. Glücklicherweise gibt es eine dynamische Möglichkeit, damit umzugehen. Es gibt eine Pseudokollatierung namens DATABASE_DEFAULT, Die die aktuelle Datenbanksortierung aufnimmt (genau wie die Standardfelder für Zwangsmaßnahmen).

    Fügen Sie am Ende der Zeile nach oben Folgendes hinzu, das mit , RecoveryModel Beginnt: COLLATE DATABASE_DEFAULT

    Führen Sie diese Abfrage aus. Sicher genug, der Fehler besagt erneut, dass es das Feld PageVerify ist, das den Konflikt hat.

  4. Für den endgültigen Test müssen wir keine der vorherigen Änderungen rückgängig machen.

    Alles, was wir jetzt tun müssen, um diese UNPIVOT Abfrage zu beheben, ist das Hinzufügen von COLLATE DATABASE_DEFAULT Am Ende der verbleibenden impliziten Kollatierungsfelder: PageVerify und RestrictedAccess. Während das Feld Collation ebenfalls eine implizite Sortierung ist, stammt dieses Feld aus der Datenbank master, die normalerweise mit der "aktuellen" Datenbank übereinstimmt. Wenn Sie jedoch sicher sein möchten, dass dies immer funktioniert, fügen Sie am Ende dieses Felds auch COLLATE DATABASE_DEFAULT Hinzu.

    Führen Sie diese Abfrage aus. Sicher genug, keine Fehler. Alles, was zur Behebung dieser Abfrage erforderlich war, war das Hinzufügen von COLLATE DATABASE_DEFAULT Am Ende von 3 Feldern (erforderlich) und möglicherweise 1 weiteren (optional).

  5. Optionaler Test: Nachdem die UNPIVOT-Abfrage nun ordnungsgemäß funktioniert, ändern Sie nur eine der Felddefinitionen, die mit CONVERT(VARCHAR(50), beginnen, in 51, Wie in: CONVERT(VARCHAR(51),.

    Führen Sie die Abfrage aus. Sie sollten den gleichen The type of column "X" conflicts with the type of other columns specified in the UNPIVOT list. - Fehler erhalten, den Sie erhalten haben, als nur die Sortierung nicht übereinstimmte.

    Es ist nicht spezifisch genug, den gleichen Fehler sowohl für Datentyp- als auch für Kollatierungsfehlanpassungen zu erhalten, um wirklich hilfreich zu sein. Da gibt es also definitiv Raum für Verbesserungen :).


Hinweis zur Abfrage mehr als zur spezifischen Frage zur Sortierung:

Da alle Quellfelder vom Datentyp NVARCHAR sind, wäre es sicherer, CONVERT alle Ausgabefelder auf NVARCHAR anstatt VARCHAR zu setzen. Möglicherweise haben Sie im Moment keine Daten, die keine Standard-ASCII-Zeichen enthalten, aber die System-Metadaten ermöglichen diese Konvertierung in NVARCHAR(128) - das ist die größte maximale Länge von allen dieser Felder - garantiert zumindest, dass es in Zukunft kein Problem für Sie oder für andere Personen gibt, die diesen Code kopieren und möglicherweise bereits einige dieser Zeichen in ihrem System haben.

7
Solomon Rutzky

Dies ist eine Problemumgehung für das spezifische Problem und keine vollständige Antwort auf die Frage. Sie können den Fehler vermeiden, indem Sie in sql_variant Anstelle von varchar(50) konvertieren:

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;

Ich habe drei Spalten hinzugefügt, um Informationen über den zugrunde liegenden Typ der Spalte OptionValue zu erhalten.

(Sample output

Wenn der Client keine sql_variant - Daten verarbeiten kann, führen Sie eine endgültige Konvertierung (oberste Ebene) in der Spalte unpvt.OptionValue In z. nvarchar(256).

5
Paul White 9

Ok, also habe ich mir das angeschaut

sp_helptext [sys.databases]

dann brach zusammen, woher die Säulen kamen. Diejenigen mit der Latin1_General_CI_AS_KS_WS - Sortierung stammen alle aus der Systemtabelle sys.syspalvalues, Die eine generische Nachschlagetabelle zu sein scheint (es ist eine Systemtabelle, daher müssen Sie eine Verbindung über den DAC herstellen, um dies zu tun sieh es dir an.).

Ich vermute, dass es auf Latin1_General_CI_AS_KS_WS Gesetzt ist, um mögliche Suchwerte zu verarbeiten. Ich kann sehen, wie nervig es wäre.

Eine andere Möglichkeit, die Definition (ursprünglich bereitgestellt von Max in einem Kommentar) anzuzeigen, ist:

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