¿Qué pasa con la recopilación de algunas columnas en sys.databases?

21

Estoy intentando ejecutar una UNPIVOTen varias columnas contenidas en sys.databasesvarias versiones de SQL Server, que van desde 2005 hasta 2012.

El UNPIVOTestá fallando con el siguiente mensaje de error:

Mensaje 8167, Nivel 16, Estado 1, Línea 48

El tipo de columna "CompatibilityLevel" entra en conflicto con el tipo de otras columnas especificadas en la lista UNPIVOT.

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

Esto está diseñado para proporcionar una lista bien formateada de opciones de base de datos para la base de datos dada, similar a:

+----------+----------------------------+----------------------------+
| Database | Configuration Item         | Value in Use               |
+----------+----------------------------+----------------------------+
| master   | RecoveryModel              | SIMPLE                     |
| master   | CompatibilityLevel         | SQL Server 2008            |
| master   | AutoClose                  | FALSE                      |
| master   | AutoCreateStatistics       | TRUE                       |
| master   | AutoShrink                 | FALSE                      |
| master   | AutoUpdateStatistics       | TRUE                       |
| master   | AutoUpdateStatisticsAsynch | FALSE                      |
| master   | CloseCursorOnCommit        | FALSE                      |
| master   | DefaultCursor              | GLOBAL                     |
| master   | ANSINULL_Default           | FALSE                      |
| master   | ANSINULLS_Enabled          | FALSE                      |
| master   | ANSIPadding_Enabled        | FALSE                      |
| master   | ANSIWarnings_Enabled       | FALSE                      |
| master   | ArithmeticAbort_Enabled    | FALSE                      |
| master   | ConcatNullYieldsNull       | FALSE                      |
| master   | CrossDBOwnerChain          | TRUE                       |
| master   | DateCorrelationOptimized   | FALSE                      |
| master   | NumericRoundAbort          | FALSE                      |
| master   | Parameterization           | SIMPLE                     |
| master   | QuotedIdentifiers_Enabled  | FALSE                      |
| master   | RecursiveTriggers_Enabled  | FALSE                      |
| master   | TrustWorthy                | TRUE                       |
| master   | VARDECIMAL_Storage         | TRUE                       |
| master   | PageVerify                 | CHECKSUM                   |
| master   | BrokerEnabled              | FALSE                      |
| master   | DatabaseReadOnly           | FALSE                      |
| master   | EncryptionEnabled          | FALSE                      |
| master   | RestrictedAccess           | MULTI_USER                 |
| master   | Collation                  | Latin1_General_CI_AS_KS_WS |
+----------+----------------------------+----------------------------+

Cuando ejecuto esto en un servidor con Latin1_General_CI_AS_KS_WSintercalación, la declaración tiene éxito. Si modifico el T-SQL para que ciertos campos tengan una COLLATEcláusula, se ejecutará en servidores que tengan otras intercalaciones.

El código que funciona en servidores con intercalaciones distintas Latin1_General_CI_AS_KS_WSes:

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;

El comportamiento observado es que los siguientes campos no observan ni la intercalación del servidor ni la intercalación de la base de datos; que están siempre presentan en Latin1_General_CI_AS_KS_WSel cotejo.

En SQL Server 2012, podemos usar sys.sp_describe_first_result_setpara obtener fácilmente metadatos sobre las columnas devueltas de una consulta en particular. Usé lo siguiente para determinar la falta de coincidencia de colación:

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;

Los resultados:

ingrese la descripción de la imagen aquí

¿Por qué la clasificación de estas columnas está establecida estáticamente?

Max Vernon
fuente

Respuestas:

17

La palabra oficial de Microsoft:

Algunas de las columnas que contienen cadenas predefinidas (como los tipos, las descripciones del sistema y las constantes) siempre se fijan a una intercalación específica Latin1_General_CI_AS_KS_WS. Esto es independiente de la clasificación de instancia / base de datos. La razón es que se trata de metadatos del sistema (no de metadatos del usuario) y, básicamente, estas cadenas se tratan sin distinción entre mayúsculas y minúsculas (como palabras clave, por lo que siempre son latinas).

Otras columnas en las tablas del sistema que contienen metadatos del usuario, como nombres de objetos, nombres de columnas, nombres de índice, nombres de inicio de sesión, etc., toman la instancia o la clasificación de la base de datos. Las columnas se clasifican según la clasificación adecuada en el momento de la instalación de SQL Server en el caso de la clasificación de instancias y en el momento de la creación de la base de datos en el caso de la clasificación de la base de datos.

Usted preguntó (énfasis mío):

¿Por qué la clasificación de estas columnas está establecida estáticamente?

La razón por la que algunas columnas están establecidas estáticamente es para que las consultas no tengan que preocuparse por la recopilación del servidor o la base de datos (más importante aún: CaSe SenSiTIviTy) para que funcionen correctamente. Esta consulta siempre funcionará independientemente de la clasificación:

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

Mientras que si la clasificación del servidor distingue entre mayúsculas y minúsculas, la consulta anterior devolvería 0 filas, al igual que esto:

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

Por ejemplo, si instala una instancia de SQL Server con SQL_Estonian_CP1257_CS_ASintercalación, ejecute lo siguiente:

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

Verá estos resultados (o algo similar, dependiendo de su versión 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

Ahora, para demostrar vistas de metadatos que heredan la clasificación de la base de datos, en lugar de heredar la clasificación del servidor de la base de datos maestra:

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

Resultados:

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

Por lo tanto, puede ver que en este caso varias columnas heredan la clasificación de la base de datos, mientras que otras están fijadas a esta clasificación latina "genérica", lo que significa que se utiliza para aislar ciertos nombres y propiedades de los problemas de mayúsculas y minúsculas como se describió anteriormente.

Si intenta realizar un UNION, por ejemplo:

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

Obtiene este error:

Mensaje 451, Nivel 16, Estado 1
No se puede resolver el conflicto de intercalación entre "Albanian_BIN" y "SQL_Estonian_CP1257_CS_AS" en el operador UNION ALL que se produce en la columna de instrucción SELECT 1.

Del mismo modo, si intenta realizar una PIVOTo UNPIVOT, las reglas son aún más estrictas (los tipos de salida deben coincidir exactamente en lugar de ser simplemente compatibles), pero el mensaje de error es mucho menos útil e incluso engañoso:

Mensaje 8167, Nivel 16, Estado 1
El tipo de columna "nombre de columna" entra en conflicto con el tipo de otras columnas especificadas en la lista UNPIVOT.

Debe evitar estos errores utilizando COLLATEcláusulas explícitas en sus consultas. Por ejemplo, la unión anterior podría ser:

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;

El único momento en que esto puede causar problemas es que obtendrá resultados confusos si una clasificación es forzada pero no contiene la misma representación de caracteres, o si se utiliza la clasificación y la clasificación forzada utiliza un orden de clasificación diferente al de la fuente.

Aaron Bertrand
fuente
7

Antecedentes sobre la precedencia de colación

El comportamiento que está viendo con respecto a la Clasificación de varios campos en las vistas de catálogo del sistema es el resultado de cómo se define cada campo y la Precedencia de clasificación.

Al mirar sys.databases, es importante tener en cuenta que no es una mesa. Mientras que en el pasado (creo que terminaba en SQL Server 2000) estas eran tablas del catálogo del sistema , ahora son vistas del catálogo del sistema . Por lo tanto, la fuente de la información en ellos no proviene necesariamente del contexto de la base de datos actual (o del contexto de la base de datos especificada cuando se trata de un objeto totalmente calificado comomaster.sys.databases ).

Tratando específicamente con sys.databases, algunos de los campos provienen de la [master]base de datos (que se creó con una clasificación basada en la clasificación predeterminada de la instancia, es decir, la clasificación a nivel de servidor), algunos de los campos son expresiones (es decir, CASEdeclaraciones), y algunos están llegando de una fuente "oculta": la [mssqlsystemresource]base de datos. Y el[mssqlsystemresource] base de datos tiene una recopilación de:Latin1_General_CI_AS_KS_WS .

El namecampo se obtiene del namecampo en master.sys.sysdbreg. Por lo tanto, este campo siempre debe estar en la recopilación de[master] base datos, que nuevamente coincidirá con la clasificación del servidor.

PERO, los siguientes campos sys.databasesprovienen del [name]campo en[mssqlsystemresource].[sys].[syspalvalues] :

  • user_access_desc
  • snapshot_isolation_state_desc
  • recovery_model_desc
  • page_verify_option_desc
  • log_reuse_wait_desc
  • container_desc

Esos campos siempre deben tener una intercalación de Latin1_General_CI_AS_KS_WS.

El collation_namecampo, sin embargo, proviene de la siguiente expresión:

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)

Aquí es donde comienza la precedencia de intercalación . Ambas opciones de salida aquí son funciones del sistema: serverproperty()y collationpropertyfromid(). La clasificación de esta expresión se considera un "valor coercible por defecto"

Cualquier variable de cadena de caracteres Transact-SQL, parámetro, literal o la salida de una función incorporada en el catálogo, o una función incorporada que no toma entradas de cadena pero produce una salida de cadena.

Si el objeto se declara en una función definida por el usuario, un procedimiento almacenado o un disparador, se le asigna la clasificación predeterminada de la base de datos en la que se crea la función, el procedimiento almacenado o el disparador. Si el objeto se declara en un lote, se le asigna la clasificación predeterminada de la base de datos actual para la conexión.

A la luz de ese segundo párrafo, dado que sys.databaseses una vista que existe en la masterbase de datos, toma la clasificación de la masterbase de datos (no la base de datos actual).

El state_desccampo también es una expresión:

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

Pero, el cotejo de esta expresión es Latin1_General_CI_AS_KS_WS. ¿Por qué? Bueno, se introduce algo nuevo en esta expresión: una referencia a un campo real: [mssqlsystemresource].[sys].[syspalvalues].[name]en esa ELSEcláusula final . Las referencias de columna se consideran "implícitas":

Una referencia de columna. La clasificación de la expresión se toma de la clasificación definida para la columna en la tabla o vista.

Por supuesto, esto abre una pregunta interesante: ¿es posible que esta expresión devuelva una intercalación diferente dependiendo de cómo CASEse evalúa? Los literales estarán en la clasificación de la base de datos donde se define este objeto, pero la ELSEcondición devuelve un valor de campo que debe conservar su clasificación original. Afortunadamente, podemos simular una prueba usando la función de administración dinámica 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

Devuelve (en una instancia configurada con una clasificación de SQL_Latin1_General_CP1_CI_ASpero que se ejecuta en una base de datos con una clasificación 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

Aquí vemos que las dos consultas que hacen referencia al campo en [msdb]la recopilación de la[msdb] base datos (que, al ser una base datos del sistema, fue determinada por la clasificación del servidor).

Relacionado con la pregunta original

El comportamiento observado es que los siguientes campos no observan ni la intercalación del servidor ni la intercalación de la base de datos; que están siempre presentan en Latin1_General_CI_AS_KS_WSel cotejo.

Su observación es puntual: esos campos siempre tienen una clasificación Latin1_General_CI_AS_KS_WS, independientemente de la clasificación del servidor o de la base de datos. Y la razón es: precedencia de colación. Esos campos provienen de una tabla en la [mssqlsystemresource]base de datos y conservarán esa clasificación inicial a menos que se anule con una COLLATEcláusula explícita, ya que tiene la mayor prioridad:

Explicit = Una expresión que se convierte explícitamente en una clasificación específica mediante el uso de una cláusula COLLATE en la expresión.

Lo explícito tiene prioridad sobre lo implícito. Implícito tiene prioridad sobre Coercible-default:
Explícito> Implícito> Coercible-default

Y la pregunta relacionada:

¿Por qué la clasificación de estas columnas está establecida estáticamente?

No es que estén establecidos estáticamente, ni que los otros campos sean de alguna manera dinámicos. Todos los campos en todas esas vistas de catálogo del sistema funcionan con las mismas reglas de precedencia de clasificación. La razón por la que parecen ser más "estáticos" que los otros campos (es decir, no cambian incluso si instala SQL Server con una clasificación predeterminada diferente, que a su vez crea las bases de datos del sistema con esa clasificación predeterminada) es que la [mssqlsystemresource]base de datos consistentemente tiene un cotejo deLatin1_General_CI_AS_KS_WS cualquier instalación de SQL Server (o eso parece). Y esto tiene sentido porque de lo contrario sería difícil para SQL Server administrarse internamente (es decir, si las reglas de clasificación y comparación utilizadas para la lógica interna cambiaran en función de la instalación).

Cómo ver estos detalles usted mismo

Si desea ver el origen de cualquier campo (s) en cualquiera de estas vistas de catálogo del sistema, simplemente haga lo siguiente:

  1. En una pestaña de consulta en SSMS, habilite la opción de consulta "Incluir plan de ejecución real" ( CTRL-M)
  2. Ejecute una consulta seleccionando un campo de una de las vistas del catálogo del sistema (recomiendo seleccionar solo un campo a la vez, ya que el plan de ejecución es ridículamente grande / complejo, incluso para un solo campo, e incluirá referencias a muchos campos que no está ' t seleccionando):

    SELECT recovery_model_desc FROM sys.databases;
  3. Vaya a la pestaña "Plan de ejecución"
  4. Haga clic derecho en el área gráfica del plan de ejecución y seleccione "Mostrar XML del plan de ejecución ..."
  5. Se abrirá una nueva pestaña en SSMS con un título similar a: Execution plan.xml
  6. Ir a la Execution plan.xmlpestaña
  7. Busque la primera aparición de una <OutputList>etiqueta (generalmente debe estar entre las líneas 10 y 20)
  8. Debería haber un <ColumnReference> etiqueta. Los atributos en esa etiqueta deben apuntar a un campo específico en una tabla o apuntar a una expresión definida más adelante en el plan.
  9. Si los atributos apuntan a un campo real, ya está listo, ya que tiene toda la información. Lo siguiente es lo que se muestra para el recovery_model_desccampo:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
  10. Si los atributos apuntan a una expresión, como si en su lugar seleccionó el state_desccampo, inicialmente encontrará:

    <ColumnReference Column="Expr1024" />
  11. En este caso, debe buscar en el resto del plan la definición Expr1024o el # que se le ocurra. Solo tenga en cuenta que podría haber varias de estas referencias, pero la definición no estará en un <OutputList>bloque. Sin embargo, tendrá un <ScalarOperator>elemento hermano que contiene la definición. Lo siguiente es lo que se muestra para el state_desccampo:

    <ScalarOperator ScalarString="CASE WHEN serverproperty('EngineEdition')=5 AND [Expr1081]=(1) THEN N'RESTORING' ELSE CASE WHEN serverproperty('EngineEdition')=5 AND CONVERT(bit,[master].[sys].[sysdbreg].[status] as [d].[status]&amp;(128),0)=(1) THEN N'COPYING' ELSE CASE WHEN serverproperty('EngineEdition')=5 AND CONVERT(bit,[master].[sys].[sysdbreg].[status] as [d].[status]&amp;(256),0)=(1) THEN N'SUSPECT' ELSE [mssqlsystemresource].[sys].[syspalvalues].[name] as [st].[name] END END END">

También se puede hacer lo mismo para verificar la fuente de las vistas de catálogo a nivel de base de datos. Hacer esto para un objeto como sys.tablesmostrará que el namecampo proviene [current_db].[sys].[sysschobjs](es por eso que tiene una clasificación que coincide con la clasificación de la base de datos), mientras que el lock_escalation_desccampo proviene [mssqlsystemresource].[sys].[syspalvalues](es por eso que tiene una clasificación Latin1_General_CI_AS_KS_WS).

Clippy dice: "Parece que quieres hacer una consulta UNPIVOT".

Ahora que sabemos por qué es la precedencia de colación y cómo funciona, apliquemos ese conocimiento a la consulta UNPIVOT.

Para una UNPIVOToperación, SQL Server parece preferir realmente (es decir, requiere) que cada campo de origen sea exactamente del mismo tipo . Por lo general, "tipo" se refiere al tipo base (es decir, VARCHAR/ NVARCHAR/ INT/ etc.) pero aquí SQL Server también incluye COLLATION. Esto no debe verse como irrazonable dado lo que controlan las intercalaciones: el conjunto de caracteres (es decir, la página de códigos) para VARCHAR y las reglas lingüísticas que determinan la equivalencia de caracteres y las combinaciones de caracteres (es decir, la normalización). La siguiente es una introducción a Mimi sobre lo que es la "normalización" de 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 '---';

Devoluciones:

---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---

Así que ahora comencemos su consulta original. Haremos algunas pruebas para ver cómo varios cambios alteran el resultado, y luego veremos cómo solo algunos cambios pueden solucionarlo.

  1. El primer error es sobre el CompatibilityLevelcampo, que es el segundo campo que no se ha dividido, y resulta ser una expresión que contiene todos los literales de cadena. Sin referencias de campo, la clasificación resultante se considera un "valor predeterminado coercible"). Los valores predeterminados coercibles toman la clasificación de la base de datos actual, digamos SQL_Latin1_General_CP1_CI_AS. Los siguientes 20 campos más o menos también son solo literales de cadena y, por lo tanto, también son valores predeterminados coercibles, por lo que no deberían estar en conflicto. Pero si miramos hacia atrás al primer campo, recovery_model_desceso viene directamente de un campo en sys.databases, lo que lo convierte en una intercalación "implícita", y esto no toma la intercalación del DB local, sino que retiene su intercalación original, que es Latin1_General_CI_AS_KS_WS( porque realmente viene del [mssqlsystemresource]DB).

    Entonces, si el campo 1 (RecoveryModel) es Latin1_General_CI_AS_KS_WS, y el campo 2 (CompatibilityLevel) es SQL_Latin1_General_CP1_CI_AS, entonces deberíamos poder forzar el campo 2 para Latin1_General_CI_AS_KS_WSque coincida con el campo 1, y luego debería aparecer el error para el campo 3 (AutoClose).

    Agregue lo siguiente al final de la CompatibilityLevellínea:
    COLLATE Latin1_General_CI_AS_KS_WS

    Y luego ejecuta la consulta. Efectivamente, el error ahora indica que es el AutoClosecampo el que tiene el conflicto.

  2. Para nuestra segunda prueba, necesitamos deshacer el cambio que acabamos de hacer (es decir, eliminar la COLLATEcláusula del final de la CompatibilityLevellínea.

    Ahora, si SQL Server realmente está evaluando en el orden en que se especifican los campos, deberíamos poder eliminar el campo 1 (RecoveryModel), lo que hará que el campo actual 2 (Nivel de compatibilidad) sea el campo que establece la clasificación maestra de la UNPIVOT resultante. Y el CompatibilityLevelcampo es un valor predeterminado coercible que toma la clasificación de la base de datos, por lo que el primer error debe ser el PageVerifycampo, que es una referencia de campo, que es una clasificación implícita que retiene la clasificación original, que en este caso es Latin1_General_CI_AS_KS_WSy no es el colación de la DB actual.

    Continúe y comente la línea que comienza , RecoveryModelen SELECT(hacia la parte superior) y luego comente la RecoveryModellínea en la UNPIVOTcláusula a continuación y elimine la coma inicial de la siguiente línea para CompatibilityLevelque no obtenga un error de sintaxis.

    Ejecute esa consulta. Efectivamente, el error ahora indica que es el PageVerifycampo el que tiene el conflicto.

  3. Para nuestra tercera prueba, necesitamos deshacer los cambios que acabamos de hacer para eliminar el RecoveryModelcampo. Así que adelante, vuelve a colocar la coma y descomenta esas otras dos líneas.

    Ahora podemos ir en la otra dirección forzando una intercalación. En lugar de cambiar la clasificación de los campos de clasificación coercibles-predeterminados (que es la mayoría de ellos), deberíamos poder cambiar los campos de clasificación implícitos a los del DB actual, ¿verdad?

    Entonces, al igual que nuestra primera prueba, deberíamos poder forzar la recopilación del campo 1 (RecoveryModel) con una COLLATEcláusula explícita . Pero si especificamos una intercalación particular y luego ejecutamos la consulta en una base de datos con una intercalación diferente, los campos de intercalación coercibles por defecto recogerán la nueva intercalación que luego entrará en conflicto con lo que estamos configurando para este primer campo. Eso parece un dolor. Afortunadamente, hay una forma dinámica de lidiar con esto. Hay una pseudo cotejo llamada DATABASE_DEFAULTque recoge el cotejo de las bases de datos actuales (al igual que los campos coercibles por defecto).

    Continúe y agregue lo siguiente al final de la línea, hacia la parte superior, que comienza con , RecoveryModel: COLLATE DATABASE_DEFAULT

    Ejecute esa consulta. Efectivamente, el error establece, una vez más, que es el PageVerifycampo el que tiene el conflicto.

  4. Para la prueba final, no necesitamos deshacer ninguno de los cambios anteriores.

    Todo lo que necesitamos hacer ahora para arreglar esta UNPIVOTconsulta es agregar COLLATE DATABASE_DEFAULTal final de los campos de colación implícitos restantes: PageVerifyy RestrictedAccess. Si bien el Collationcampo también es una recopilación implícita, ese campo proviene de la masterbase de datos, que generalmente está en línea con la base de datos "actual". Pero, si desea estar seguro para que esto siempre funcione, entonces agregue también COLLATE DATABASE_DEFAULTal final de ese campo.

    Ejecute esa consulta. Efectivamente, no hay errores. Todo lo que se necesitó para solucionar esta consulta fue agregar COLLATE DATABASE_DEFAULTal final de 3 campos (requerido) y quizás 1 más (opcional).

  5. Prueba Opcional: Ahora que por fin tenemos la consulta UNPIVOT de funcionar correctamente, el cambio sólo uno de cualquiera de las definiciones de campo que comienzan con CONVERT(VARCHAR(50),a ser, en cambio 51, como en: CONVERT(VARCHAR(51),.

    Ejecute la consulta. Debería obtener el mismo The type of column "X" conflicts with the type of other columns specified in the UNPIVOT list.error que recibió cuando era solo la intercalación que no coincidía.

    Obtener el mismo error para los desajustes de tipo de datos y de intercalación no es lo suficientemente específico como para ser realmente útil. Así que definitivamente hay margen de mejora allí :).


Nota relacionada con la consulta más que con la pregunta específica sobre clasificación:

Dado que todos los campos de origen son del tipo de datos NVARCHAR, sería más seguro para CONVERTtodos los campos de salida que en NVARCHARlugar de VARCHAR. Es posible que no esté lidiando con datos en el momento que tienen caracteres ASCII no estándar, pero los metadatos del sistema sí permiten que se conviertan a NVARCHAR(128), que es la longitud máxima más grande de cualquiera de esos campos, al menos garantiza que no habrá un problema para usted en el futuro, o para cualquier otra persona que copie este código que ya tenga algunos de esos caracteres en su sistema.

Solomon Rutzky
fuente
5

Esta es una solución para el problema específico en lugar de una respuesta completa a la pregunta. Puede evitar el error convirtiendo en sql_variantlugar de 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;

He agregado tres columnas para obtener información sobre el tipo subyacente de la OptionValuecolumna.

Salida de muestra

Si el cliente no puede manejar los sql_variantdatos, realice una conversión final (nivel superior) en la unpvt.OptionValuecolumna, por ej nvarchar(256).

Paul White dice GoFundMonica
fuente
4

Ok, eche un vistazo a

sp_helptext [sys.databases]

luego se desglosó de dónde venían las columnas. Los que tienen la Latin1_General_CI_AS_KS_WSclasificación provienen de la tabla del sistema, sys.syspalvaluesque parece ser una tabla de búsqueda genérica (es una tabla del sistema, por lo que tendrá que conectarse a través del DAC para verla).

Supongo que está configurado Latin1_General_CI_AS_KS_WSpara manejar cualquier posible valor de búsqueda. Sin embargo, puedo ver cómo sería molesto.

Otra forma de ver la definición (originalmente proporcionada por Max en un comentario) es:

SELECT ObjectSchema = s.name
    , ObjectName = o.name
    , ObjectDefinition = sm.definition
FROM master.sys.all_sql_modules sm
    INNER JOIN master.sys.system_objects o ON sm.object_id = o.object_id
    INNER JOIN master.sys.schemas s ON o.schema_id = s.schema_id
WHERE s.name = 'sys' 
    AND o.name = 'databases';`
Kenneth Fisher
fuente