Qu'en est-il du classement de certaines colonnes dans sys.databases?


21

J'essaie d'exécuter une UNPIVOTsur diverses colonnes contenues dans sys.databasesdifférentes versions de SQL Server, allant de 2005 à 2012.

Le UNPIVOTéchoue avec le message d'erreur suivant:

Msg 8167, niveau 16, état 1, ligne 48

Le type de colonne "CompatibilityLevel" entre en conflit avec le type des autres colonnes spécifiées dans la liste UNPIVOT.

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

Ceci est conçu pour fournir une liste bien formatée d'options de base de données pour la base de données donnée, semblable à:

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

Lorsque j'exécute cela sur un serveur avec Latin1_General_CI_AS_KS_WSclassement, l'instruction réussit. Si je modifie le T-SQL pour que certains champs aient une COLLATEclause, il fonctionnera sur des serveurs qui ont d'autres classements.

Le code qui fonctionne sur les serveurs avec des classements autres que Latin1_General_CI_AS_KS_WSest:

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;

Le comportement observé est que les champs suivants n'observent ni le classement du serveur, ni le classement de la base de données; ils sont toujours présentés en Latin1_General_CI_AS_KS_WScollation.

Sur SQL Server 2012, nous pouvons utiliser sys.sp_describe_first_result_setpour obtenir facilement des métadonnées sur les colonnes renvoyées par une requête particulière. J'ai utilisé ce qui suit pour déterminer la non-concordance de classement:

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;

Les resultats:

entrez la description de l'image ici

Pourquoi le classement de ces colonnes est-il défini statiquement?

Réponses:


17

Le mot officiel de Microsoft:

Certaines des colonnes qui contiennent des chaînes prédéfinies (comme les types, les descriptions de système et les constantes) sont toujours fixées à un classement spécifique - Latin1_General_CI_AS_KS_WS. Ceci est indépendant du classement d'instance / base de données. La raison en est qu'il s'agit de métadonnées système (et non de métadonnées utilisateur) et, fondamentalement, ces chaînes sont traitées indépendamment de la casse (comme les mots clés, donc toujours en latin).

D'autres colonnes dans les tables système qui contiennent des métadonnées utilisateur telles que les noms d'objets, les noms de colonnes, les noms d'index, les noms de connexion, etc. prennent le classement d'instance ou de base de données. Les colonnes sont regroupées pour un classement correct au moment de l'installation de SQL Server en cas de classement d'instance et au moment de la création de la base de données en cas de classement de la base de données.

Vous avez demandé (soulignement le mien):

Pourquoi le classement de ces colonnes est-il défini statiquement?

La raison pour laquelle certaines colonnes sont statiquement définies est que les requêtes n'ont pas à se soucier du classement du serveur ou de la base de données (plus important encore: CaSe SenSiTIviTy) pour fonctionner correctement. Cette requête fonctionnera toujours quel que soit le classement:

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

Alors que si le classement du serveur était sensible à la casse, la requête ci-dessus retournerait 0 lignes, tout comme ceci:

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

Par exemple, si vous installez une instance de SQL Server avec SQL_Estonian_CP1257_CS_ASclassement, exécutez ensuite ce qui suit:

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

Vous verrez ces résultats (ou quelque chose de similaire, selon votre version de 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

Maintenant, pour illustrer les vues de métadonnées qui héritent du classement de la base de données, plutôt que d'hériter du classement du serveur de la base de données master:

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

Résultats:

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

Ainsi, vous pouvez voir que dans ce cas, plusieurs colonnes héritent du classement de la base de données, tandis que d'autres sont fixées sur ce classement Latin1 "générique", ce qui signifie qu'il est utilisé pour isoler certains noms et propriétés des problèmes de respect de la casse comme décrit ci-dessus.

Si vous essayez d'effectuer un UNION, par exemple:

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

Vous obtenez cette erreur:

Msg 451, niveau 16, état 1
Impossible de résoudre le conflit de classement entre "Albanian_BIN" et "SQL_Estonian_CP1257_CS_AS" dans l'opérateur UNION ALL survenant dans la colonne 1 de l'instruction SELECT.

De même, si vous essayez d'effectuer un PIVOTou UNPIVOT, les règles sont encore plus strictes (les types de sortie doivent tous correspondre exactement plutôt que simplement être compatibles), mais le message d'erreur est beaucoup moins utile et même trompeur:

Msg 8167, niveau 16, état 1
Le type de colonne "nom de colonne" entre en conflit avec le type des autres colonnes spécifiées dans la liste UNPIVOT.

Vous devez contourner ces erreurs à l'aide de COLLATEclauses explicites dans vos requêtes. Par exemple, l'union ci-dessus pourrait être:

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;

La seule fois où cela peut provoquer des problèmes est que la sortie sera confuse si un classement est forcé mais ne contient pas la même représentation de caractères, ou si le tri est utilisé et que le classement forcé utilise un ordre de tri différent de celui de la source.


7

Contexte de la priorité de classement

Le comportement que vous voyez en ce qui concerne le classement de divers champs dans les vues de catalogue système est le résultat de la définition de chaque champ et de la priorité de classement.

En regardant sys.databases, il est important de garder à l'esprit qu'il ne s'agit pas d'une table. Alors que dans le passé (je pense que cela se terminait à SQL Server 2000), il s'agissait de tables de catalogue système , ce sont désormais des vues de catalogue système . Par conséquent, la source des informations qu'ils contiennent ne provient pas nécessairement du contexte actuel de la base de données (ou du contexte de la base de données spécifiée lorsqu'il s'agit d'un objet pleinement qualifié tel que master.sys.databases).

En ce qui concerne spécifiquement sys.databases, certains des champs proviennent de la [master]base de données (qui a été créée avec un classement basé sur le classement par défaut de l'instance - c'est-à-dire le classement au niveau du serveur), certains des champs sont des expressions (c'est-à-dire des CASEinstructions), et certains viennent à partir d'une source "cachée": la [mssqlsystemresource]base de données. Et la [mssqlsystemresource]base de données a une collation de: Latin1_General_CI_AS_KS_WS.

Le namechamp provient du namechamp dans master.sys.sysdbreg. Ce champ doit donc toujours être dans le classement de la [master]base de données, qui correspondra à nouveau au classement du serveur.

MAIS, les champs suivants sys.databasesproviennent du [name]champ de [mssqlsystemresource].[sys].[syspalvalues]:

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

Ces champs doivent toujours avoir un classement de Latin1_General_CI_AS_KS_WS.

Le collation_namechamp provient cependant de l'expression suivante:

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)

C'est là que la priorité de classement commence à apparaître. Les deux options de sortie ici sont les fonctions système: serverproperty()et collationpropertyfromid(). Le classement de cette expression est considéré comme un "Coercible-default":

Toute variable, paramètre, littéral ou chaîne de caractères Transact-SQL ou la sortie d'une fonction intégrée de catalogue, ou une fonction intégrée qui n'accepte pas les entrées de chaîne mais produit une sortie de chaîne.

Si l'objet est déclaré dans une fonction, une procédure stockée ou un déclencheur défini par l'utilisateur, l'objet est affecté au classement par défaut de la base de données dans laquelle la fonction, la procédure stockée ou le déclencheur est créé. Si l'objet est déclaré dans un lot, l'objet est affecté au classement par défaut de la base de données actuelle pour la connexion.

À la lumière de ce 2e paragraphe, puisqu'il sys.databasess'agit d'une vue qui existe dans la masterbase de données, elle prend en charge le classement de la masterbase de données (pas la base de données actuelle).

Le state_descchamp est aussi une expression:

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

Mais, le classement sur cette expression est Latin1_General_CI_AS_KS_WS. Pourquoi? Eh bien, quelque chose de nouveau est introduit dans cette expression: une référence à un champ réel: [mssqlsystemresource].[sys].[syspalvalues].[name]dans cette dernière ELSEclause. Les références de colonne sont considérées comme "implicites":

Une référence de colonne. Le classement de l'expression est tiré du classement défini pour la colonne dans la table ou la vue.

Bien sûr, cela ouvre une question intéressante: est-il possible que cette expression retourne un classement différent selon la façon dont le CASEest évalué? Les littéraux seront dans le classement de la base de données où cet objet est défini, mais la ELSEcondition renvoie une valeur de champ qui doit conserver son classement d'origine. Heureusement, nous pouvons simuler un test à l'aide de la fonction de gestion dynamique 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

Renvoie (sur une instance configurée avec un classement de SQL_Latin1_General_CP1_CI_ASmais fonctionnant dans une base de données avec un classement de 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

Nous voyons ici que les deux requêtes qui référencent le champ en [msdb]prennent sur le classement de la [msdb]base de données (qui, étant une base de données système, a été déterminée par le classement du serveur).

Retour à la question d'origine

Le comportement observé est que les champs suivants n'observent ni le classement du serveur, ni le classement de la base de données; ils sont toujours présentés en Latin1_General_CI_AS_KS_WScollation.

Votre observation est précise: ces champs ont toujours un classement Latin1_General_CI_AS_KS_WS, indépendamment du classement du serveur ou de la base de données. Et la raison en est: Priorité de classement. Ces champs proviennent d'une table de la [mssqlsystemresource]base de données et conserveront ce classement initial, sauf s'ils sont remplacés par une COLLATEclause explicite , car celle-ci a la priorité la plus élevée:

Explicit = Expression qui est explicitement convertie en un classement spécifique à l'aide d'une clause COLLATE dans l'expression.

L'explicite prime sur l'implicite. Implicite a priorité sur Coercible-default:
Explicit> Implicit> Coercible-default

Et la question connexe:

Pourquoi le classement de ces colonnes est-il défini statiquement?

Ce n'est pas qu'ils soient statiquement définis, ni que les autres champs soient en quelque sorte dynamiques. Tous les champs de toutes ces vues de catalogue système fonctionnent selon les mêmes règles de priorité de classement. La raison pour laquelle ils semblent être plus "statiques" que les autres champs (c'est-à-dire qu'ils ne changent pas même si vous installez SQL Server avec un classement par défaut différent, qui à son tour crée les bases de données système avec ce classement par défaut) est que la [mssqlsystemresource]base de données est cohérente a un classement de Latin1_General_CI_AS_KS_WStoutes les installations de SQL Server (ou alors il apparaît certainement). Et cela a du sens car sinon il serait difficile pour SQL Server de se gérer en interne (c'est-à-dire si les règles de tri et de comparaison utilisées pour la logique interne changeaient en fonction de l'installation).

Comment voir ces détails vous-même

Si vous souhaitez voir la source de tout champ dans l'une de ces vues de catalogue système, procédez comme suit:

  1. Dans un onglet de requête dans SSMS, activez l'option de requête "Inclure le plan d'exécution réel" ( CTRL-M)
  2. Exécutez une requête en sélectionnant un champ dans l'une des vues du catalogue système (je recommande de sélectionner un seul champ à la fois car le plan d'exécution est ridiculement grand / complexe, même pour un seul champ, et inclura des références à de nombreux champs que vous n'êtes pas '' t sélection):

    SELECT recovery_model_desc FROM sys.databases;
  3. Accédez à l'onglet "Plan d'exécution"
  4. Faites un clic droit dans la zone graphique du plan d'exécution et sélectionnez «Afficher le plan d'exécution XML ...»
  5. Un nouvel onglet dans SSMS s'ouvrira avec un titre similaire à: Execution plan.xml
  6. Allez dans l' Execution plan.xmlonglet
  7. Recherchez la première occurrence d'une <OutputList>balise (elle doit être comprise entre les lignes 10 et 20 en général)
  8. Il devrait y avoir une <ColumnReference>balise. Les attributs de cette balise doivent soit pointer vers un champ spécifique dans une table, soit pointer vers une expression définie plus loin dans le plan.
  9. Si les attributs pointent vers un champ réel, vous avez terminé car il contient toutes les informations. Voici ce qui apparaît sur le recovery_model_descterrain:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
  10. Si les attributs pointent vers une expression, par exemple si vous avez sélectionné le state_descchamp à la place, vous trouverez initialement:

    <ColumnReference Column="Expr1024" />
  11. Dans ce cas, vous devez parcourir le reste du plan pour la définition Expr1024ou quoi que ce soit. Gardez juste à l'esprit qu'il pourrait y avoir plusieurs de ces références, mais la définition ne sera pas dans un <OutputList>bloc. Cependant, il aura un <ScalarOperator>élément frère qui contient la définition. Voici ce qui apparaît sur le state_descterrain:

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

La même chose peut être effectuée pour vérifier la source des vues de catalogue au niveau de la base de données. Faire cela pour un objet comme sys.tablesmontrera que le namechamp vient [current_db].[sys].[sysschobjs](c'est pourquoi il a un classement correspondant au classement de la base de données), tandis que le lock_escalation_descchamp vient [mssqlsystemresource].[sys].[syspalvalues](c'est pourquoi il a un classement de Latin1_General_CI_AS_KS_WS).

Clippy dit: "Il semble que vous souhaitiez effectuer une requête UNPIVOT."

Maintenant que nous savons pourquoi est la priorité de classement et comment cela fonctionne, appliquons ces connaissances à la requête UNPIVOT.

Pour une UNPIVOTopération, SQL Server semble vraiment préférer (ce qui signifie: nécessite) que chaque champ source soit exactement du même type . Habituellement, "type" fait référence au type de base (c'est-à VARCHAR- dire / NVARCHAR/ INT/ etc) mais ici, SQL Server inclut également la COLLATION. Cela ne doit pas être considéré comme déraisonnable compte tenu de ce que les classements contrôlent: le jeu de caractères (c'est-à-dire la page de codes) pour VARCHAR, et les règles linguistiques qui déterminent l'équivalence des caractères et les combinaisons de caractères (c'est-à-dire la normalisation). Ce qui suit est un mimi-primer sur ce qu'est la "normalisation" 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 '---';

Retour:

---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---

Alors maintenant, commençons votre requête d'origine. Nous ferons quelques tests pour voir comment divers changements modifient le résultat, puis nous verrons comment quelques changements peuvent le corriger.

  1. La première erreur concerne le CompatibilityLevelchamp, qui est le deuxième champ à ne pas pivoter, et se trouve être une expression contenant tous les littéraux de chaîne. Sans référence de champ, le classement résultant est considéré comme un "défaut coercitif"). Les coercitifs par défaut prennent en charge le classement de la base de données actuelle, disons SQL_Latin1_General_CP1_CI_AS. Les 20 champs suivants sont également uniquement des littéraux de chaîne et sont donc également des valeurs par défaut coercitives, donc ils ne devraient pas être en conflit. Mais si nous regardons le premier champ, recovery_model_descqui provient directement d'un champ dans sys.databases, ce qui en fait un classement "implicite", et cela ne prend pas le classement de la base de données locale, mais conserve à la place son classement d'origine, qui est Latin1_General_CI_AS_KS_WS( parce que ça vient vraiment de la [mssqlsystemresource]DB).

    Donc, si le champ 1 (RecoveryModel) l'est Latin1_General_CI_AS_KS_WSet que le champ 2 (CompatibilityLevel) l'est SQL_Latin1_General_CP1_CI_AS, nous devrions être en mesure de forcer le champ 2 à correspondre Latin1_General_CI_AS_KS_WSau champ 1, puis l'erreur devrait apparaître pour le champ 3 (AutoClose).

    Ajoutez ce qui suit à la fin de la CompatibilityLevelligne:
    COLLATE Latin1_General_CI_AS_KS_WS

    Et puis exécutez la requête. Effectivement, l'erreur indique maintenant que c'est le AutoClosechamp qui a le conflit.

  2. Pour notre deuxième test, nous devons annuler la modification que nous venons d'apporter (c'est-à-dire supprimer la COLLATEclause à la fin de la CompatibilityLevelligne.

    Maintenant, si SQL Server évalue vraiment dans l'ordre dans lequel les champs sont spécifiés, nous devrions pouvoir supprimer le champ 1 (RecoveryModel), ce qui fera que le champ actuel 2 (CompatibilityLevel) sera le champ qui définit le classement principal de UNPIVOT résultant. Et le CompatibilityLevelchamp est un défaut coercitif qui prend le classement de la base de données, donc la première erreur doit être le PageVerifychamp, qui est une référence de champ, qui est un classement implicite conservant le classement d'origine, qui dans ce cas est Latin1_General_CI_AS_KS_WSet qui n'est pas le classement de la DB actuelle.

    Alors allez-y et commentez la ligne commençant par , RecoveryModeldans SELECT(vers le haut), puis commentez la RecoveryModelligne dans la UNPIVOTclause ci-dessous et supprimez la virgule de début de la ligne suivante pour CompatibilityLevelque vous n'obteniez pas d'erreur de syntaxe.

    Exécutez cette requête. Effectivement, l'erreur indique maintenant que c'est le PageVerifychamp qui a le conflit.

  3. Pour notre troisième test, nous devons annuler les modifications que nous venons de faire pour supprimer le RecoveryModelchamp. Alors allez-y et remettez la virgule, et décommentez ces deux autres lignes.

    Maintenant, nous pouvons aller dans l'autre sens en forçant un classement. Plutôt que de changer le classement des champs de classement coercible par défaut (qui est la plupart d'entre eux), nous devrions pouvoir changer les champs de classement implicites en celui de la base de données actuelle, non?

    Ainsi, tout comme notre premier test, nous devrions être en mesure de forcer le classement du champ 1 (RecoveryModel) avec une COLLATEclause explicite . Mais si nous spécifions un classement particulier, puis exécutons la requête dans une base de données avec un classement différent, les champs de classement coercible par défaut prendront le nouveau classement qui entrera alors en conflit avec ce à quoi nous définissons ce premier champ. Cela semble être une douleur. Heureusement, il existe un moyen dynamique de résoudre ce problème. Il existe un pseudo classement appelé DATABASE_DEFAULTqui récupère le classement actuel des bases de données (tout comme les champs coercitifs par défaut).

    Allez-y et ajoutez ce qui suit à la fin de la ligne, vers le haut, qui commence par , RecoveryModel: COLLATE DATABASE_DEFAULT

    Exécutez cette requête. Effectivement, l'erreur indique, encore une fois, que c'est le PageVerifychamp qui a le conflit.

  4. Pour le test final, nous n'avons pas besoin d'annuler les modifications précédentes.

    Tout ce que nous devons faire maintenant pour corriger cette UNPIVOTrequête est d'ajouter le COLLATE DATABASE_DEFAULTà la fin des champs de classement implicite restants: PageVerifyet RestrictedAccess. Bien que le Collationchamp soit également un classement implicite, ce champ provient de la masterbase de données, qui est généralement conforme à la base de données "actuelle". Mais, si vous voulez être sûr pour que cela fonctionne toujours, alors allez-y, ajoutez également COLLATE DATABASE_DEFAULTla fin de ce champ.

    Exécutez cette requête. Effectivement, pas d'erreurs. Tout ce qu'il a fallu pour corriger cette requête était d'ajouter COLLATE DATABASE_DEFAULTà la fin de 3 champs (requis) et peut-être 1 de plus (facultatif).

  5. Essai en option: Maintenant que nous avons enfin la requête UNPIVOT fonctionne correctement, il suffit de changer l' un de l' une des définitions sur le terrain en commençant par CONVERT(VARCHAR(50),à la place être 51, comme dans: CONVERT(VARCHAR(51),.

    Exécutez la requête. Vous devriez obtenir la même The type of column "X" conflicts with the type of other columns specified in the UNPIVOT list.erreur que celle obtenue lorsque seul le classement ne correspondait pas.

    Obtenir la même erreur pour les incompatibilités de type de données et de classement n'est pas suffisamment spécifique pour être vraiment utile. Il y a donc certainement place à amélioration :).


Remarque relative à la requête plus qu'à la question spécifique sur le classement:

Étant donné que tous les champs source sont du type de données NVARCHAR, il serait plus sûr CONVERTde NVARCHARremplacer tous les champs de sortie par VARCHAR. Vous ne traitez peut-être pas actuellement avec des données contenant des caractères ASCII non standard, mais les métadonnées du système permettent leur conversion en NVARCHAR(128)- qui est la plus grande longueur maximale de l'un de ces champs - au moins garantit qu'il n'y aura pas de problème pour vous à l'avenir, ou pour toute autre personne copiant ce code qui pourrait déjà avoir certains de ces caractères dans leur système.


5

Il s'agit d'une solution de contournement pour le problème spécifique plutôt que d'une réponse complète à la question. Vous pouvez éviter l'erreur en effectuant une conversion sql_variantplutôt que 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;

J'ai ajouté trois colonnes pour plus d'informations sur le type sous-jacent de la OptionValuecolonne.

Exemple de sortie

Si le client ne peut pas gérer les sql_variantdonnées, effectuez une conversion finale (niveau supérieur) sur la unpvt.OptionValuecolonne, par exemple nvarchar(256).


4

Ok, donc j'ai jeté un coup d'œil à

sp_helptext [sys.databases]

puis est tombé en panne d'où provenaient les colonnes. Ceux avec le Latin1_General_CI_AS_KS_WSclassement proviennent tous de la table système sys.syspalvaluesqui semble être une table de recherche générique (c'est une table système, vous devrez donc vous connecter via le DAC pour le voir.).

Je suppose qu'il est configuré Latin1_General_CI_AS_KS_WSpour gérer toutes les valeurs de recherche possibles. Je peux voir à quel point ce serait ennuyeux.

Une autre façon de voir la définition (initialement fournie par Max dans un commentaire) est:

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';`
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.