มีอะไรขึ้นอยู่กับการเรียงคอลัมน์บางคอลัมน์ใน sys.database?


21

ฉันพยายามเรียกใช้UNPIVOTคอลัมน์ต่างๆที่มีอยู่ในsys.databasesSQL Server เวอร์ชันต่างๆตั้งแต่ปี 2005 ถึง 2012

ความUNPIVOTล้มเหลวพร้อมกับข้อความแสดงข้อผิดพลาดต่อไปนี้:

ข่าวสารเกี่ยวกับ 8167 ระดับ 16 สถานะ 1 สาย 48

ประเภทของคอลัมน์ "CompatibilityLevel" ขัดแย้งกับประเภทของคอลัมน์อื่น ๆ ที่ระบุในรายการ UNPIVOT

T-SQL:

DECLARE @dbname SYSNAME;
SET @dbname = DB_NAME();

SELECT [Database]            = unpvt.DatabaseName
    , [Configuration Item]   = unpvt.OptionName
    , [Configuration Value]  = unpvt.OptionValue
FROM (
    SELECT 
        DatabaseName = name 
        , RecoveryModel                 = CONVERT(VARCHAR(50), d.recovery_model_desc)
        , CompatibilityLevel            = CONVERT(VARCHAR(50), CASE d.[compatibility_level] WHEN 70 THEN 'SQL Server 7' WHEN 80 THEN 'SQL Server 2000' WHEN 90 THEN 'SQL Server 2005' WHEN 100 THEN 'SQL Server 2008' WHEN 110 THEN 'SQL Server 2012' WHEN 120 THEN 'SQL Server 2014' ELSE 'UNKNOWN' END)
        , AutoClose                     = CONVERT(VARCHAR(50), CASE d.is_auto_close_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoCreateStatistics          = CONVERT(VARCHAR(50), CASE d.is_auto_create_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoShrink                    = CONVERT(VARCHAR(50), CASE d.is_auto_shrink_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoUpdateStatistics          = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoUpdateStatisticsAsynch    = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_async_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , CloseCursorOnCommit           = CONVERT(VARCHAR(50), CASE d.is_cursor_close_on_commit_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , DefaultCursor                 = CONVERT(VARCHAR(50), CASE d.is_local_cursor_default WHEN 1 THEN 'LOCAL' ELSE 'GLOBAL' END)
        , ANSINULL_Default              = CONVERT(VARCHAR(50), CASE d.is_ansi_null_default_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ANSINULLS_Enabled             = CONVERT(VARCHAR(50), CASE d.is_ansi_nulls_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ANSIPadding_Enabled           = CONVERT(VARCHAR(50), CASE d.is_ansi_padding_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ANSIWarnings_Enabled          = CONVERT(VARCHAR(50), CASE d.is_ansi_warnings_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ArithmeticAbort_Enabled       = CONVERT(VARCHAR(50), CASE d.is_arithabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ConcatNullYieldsNull          = CONVERT(VARCHAR(50), CASE d.is_concat_null_yields_null_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , CrossDBOwnerChain             = CONVERT(VARCHAR(50), CASE d.is_db_chaining_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , DateCorrelationOptimized      = CONVERT(VARCHAR(50), CASE d.is_date_correlation_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , NumericRoundAbort             = CONVERT(VARCHAR(50), CASE d.is_numeric_roundabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , [Parameterization]            = CONVERT(VARCHAR(50), CASE d.is_parameterization_forced WHEN 0 THEN 'SIMPLE' ELSE 'FORCED' END)
        , QuotedIdentifiers_Enabled     = CONVERT(VARCHAR(50), CASE d.is_quoted_identifier_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , RecursiveTriggers_Enabled     = CONVERT(VARCHAR(50), CASE d.is_recursive_triggers_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , [TrustWorthy]                 = CONVERT(VARCHAR(50), CASE d.is_trustworthy_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , VARDECIMAL_Storage            = CONVERT(VARCHAR(50), 'TRUE')
        , PageVerify                    = CONVERT(VARCHAR(50), page_verify_option_desc  )
        , BrokerEnabled                 = CONVERT(VARCHAR(50), CASE d.is_broker_enabled WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , DatabaseReadOnly              = CONVERT(VARCHAR(50), CASE d.is_read_only WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , EncryptionEnabled             = CONVERT(VARCHAR(50), CASE d.is_encrypted WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , RestrictedAccess              = CONVERT(VARCHAR(50), user_access_desc)
        , Collation                     = CONVERT(VARCHAR(50), d.collation_name)
    FROM sys.databases d
    WHERE name = @dbname
        OR @dbname IS NULL
    ) src
UNPIVOT
(
    OptionValue FOR OptionName IN
    (
        RecoveryModel
        , CompatibilityLevel
        , AutoClose
        , AutoCreateStatistics 
        , AutoShrink 
        , AutoUpdateStatistics 
        , AutoUpdateStatisticsAsynch 
        , CloseCursorOnCommit 
        , DefaultCursor 
        , ANSINULL_Default 
        , ANSINULLS_Enabled 
        , ANSIPadding_Enabled 
        , ANSIWarnings_Enabled 
        , ArithmeticAbort_Enabled 
        , ConcatNullYieldsNull 
        , CrossDBOwnerChain 
        , DateCorrelationOptimized 
        , NumericRoundAbort 
        , [Parameterization] 
        , QuotedIdentifiers_Enabled 
        , RecursiveTriggers_Enabled 
        , [TrustWorthy] 
        , VARDECIMAL_Storage 
        , PageVerify 
        , BrokerEnabled 
        , DatabaseReadOnly 
        , EncryptionEnabled 
        , RestrictedAccess 
        , Collation
    )
) AS unpvt;

สิ่งนี้ได้รับการออกแบบมาเพื่อให้รายการตัวเลือกฐานข้อมูลที่จัดรูปแบบไว้อย่างดีสำหรับฐานข้อมูลที่กำหนดคล้ายกับ:

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

เมื่อฉันเรียกใช้สิ่งนี้ในเซิร์ฟเวอร์ที่มีการLatin1_General_CI_AS_KS_WSจัดเรียงคำสั่งจะสำเร็จ หากฉันแก้ไข T-SQL เพื่อให้บางฟิลด์มีส่วนCOLLATEคำสั่งจะทำงานบนเซิร์ฟเวอร์ที่มีการเปรียบเทียบอื่น ๆ

รหัสที่ทำงานบนเซิร์ฟเวอร์ที่มีการเปรียบเทียบนอกเหนือจากLatin1_General_CI_AS_KS_WS:

DECLARE @dbname SYSNAME;
SET @dbname = DB_NAME();

SELECT [Database]            = unpvt.DatabaseName
    , [Configuration Item]   = unpvt.OptionName
    , [Configuration Value]  = unpvt.OptionValue
FROM (
    SELECT 
        DatabaseName = name 
        , RecoveryModel                 = CONVERT(VARCHAR(50), d.recovery_model_desc) COLLATE SQL_Latin1_General_CP1_CI_AS
        , CompatibilityLevel            = CONVERT(VARCHAR(50), CASE d.[compatibility_level] WHEN 70 THEN 'SQL Server 7' WHEN 80 THEN 'SQL Server 2000' WHEN 90 THEN 'SQL Server 2005' WHEN 100 THEN 'SQL Server 2008' WHEN 110 THEN 'SQL Server 2012' WHEN 120 THEN 'SQL Server 2014' ELSE 'UNKNOWN' END) 
        , AutoClose                     = CONVERT(VARCHAR(50), CASE d.is_auto_close_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoCreateStatistics          = CONVERT(VARCHAR(50), CASE d.is_auto_create_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoShrink                    = CONVERT(VARCHAR(50), CASE d.is_auto_shrink_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoUpdateStatistics          = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , AutoUpdateStatisticsAsynch    = CONVERT(VARCHAR(50), CASE d.is_auto_update_stats_async_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , CloseCursorOnCommit           = CONVERT(VARCHAR(50), CASE d.is_cursor_close_on_commit_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , DefaultCursor                 = CONVERT(VARCHAR(50), CASE d.is_local_cursor_default WHEN 1 THEN 'LOCAL' ELSE 'GLOBAL' END)
        , ANSINULL_Default              = CONVERT(VARCHAR(50), CASE d.is_ansi_null_default_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ANSINULLS_Enabled             = CONVERT(VARCHAR(50), CASE d.is_ansi_nulls_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ANSIPadding_Enabled           = CONVERT(VARCHAR(50), CASE d.is_ansi_padding_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ANSIWarnings_Enabled          = CONVERT(VARCHAR(50), CASE d.is_ansi_warnings_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ArithmeticAbort_Enabled       = CONVERT(VARCHAR(50), CASE d.is_arithabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , ConcatNullYieldsNull          = CONVERT(VARCHAR(50), CASE d.is_concat_null_yields_null_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , CrossDBOwnerChain             = CONVERT(VARCHAR(50), CASE d.is_db_chaining_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , DateCorrelationOptimized      = CONVERT(VARCHAR(50), CASE d.is_date_correlation_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , NumericRoundAbort             = CONVERT(VARCHAR(50), CASE d.is_numeric_roundabort_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , [Parameterization]            = CONVERT(VARCHAR(50), CASE d.is_parameterization_forced WHEN 0 THEN 'SIMPLE' ELSE 'FORCED' END)
        , QuotedIdentifiers_Enabled     = CONVERT(VARCHAR(50), CASE d.is_quoted_identifier_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , RecursiveTriggers_Enabled     = CONVERT(VARCHAR(50), CASE d.is_recursive_triggers_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , [TrustWorthy]                 = CONVERT(VARCHAR(50), CASE d.is_trustworthy_on WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , VARDECIMAL_Storage            = CONVERT(VARCHAR(50), 'TRUE')
        , PageVerify                    = CONVERT(VARCHAR(50), page_verify_option_desc  ) COLLATE SQL_Latin1_General_CP1_CI_AS
        , BrokerEnabled                 = CONVERT(VARCHAR(50), CASE d.is_broker_enabled WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , DatabaseReadOnly              = CONVERT(VARCHAR(50), CASE d.is_read_only WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , EncryptionEnabled             = CONVERT(VARCHAR(50), CASE d.is_encrypted WHEN 0 THEN 'FALSE' ELSE 'TRUE' END)
        , RestrictedAccess              = CONVERT(VARCHAR(50), user_access_desc) COLLATE SQL_Latin1_General_CP1_CI_AS
        , Collation                     = CONVERT(VARCHAR(50), d.collation_name)
    FROM sys.databases d
    WHERE name = @dbname
        OR @dbname IS NULL
    ) src
UNPIVOT
(
    OptionValue FOR OptionName IN
    (
        RecoveryModel
        , CompatibilityLevel
        , AutoClose
        , AutoCreateStatistics 
        , AutoShrink 
        , AutoUpdateStatistics 
        , AutoUpdateStatisticsAsynch 
        , CloseCursorOnCommit 
        , DefaultCursor 
        , ANSINULL_Default 
        , ANSINULLS_Enabled 
        , ANSIPadding_Enabled 
        , ANSIWarnings_Enabled 
        , ArithmeticAbort_Enabled 
        , ConcatNullYieldsNull 
        , CrossDBOwnerChain 
        , DateCorrelationOptimized 
        , NumericRoundAbort 
        , [Parameterization] 
        , QuotedIdentifiers_Enabled 
        , RecursiveTriggers_Enabled 
        , [TrustWorthy] 
        , VARDECIMAL_Storage 
        , PageVerify 
        , BrokerEnabled 
        , DatabaseReadOnly 
        , EncryptionEnabled 
        , RestrictedAccess 
        , Collation
    )
) AS unpvt;

พฤติกรรมที่สังเกตได้คือฟิลด์ต่อไปนี้ไม่ได้สังเกตว่าการเปรียบเทียบเซิร์ฟเวอร์หรือการเปรียบเทียบฐานข้อมูล พวกเขาจะถูกนำเสนอเสมอในการLatin1_General_CI_AS_KS_WSเปรียบเทียบ

ใน SQL Server 2012 เราสามารถใช้sys.sp_describe_first_result_setเพื่อรับข้อมูลเมตาเกี่ยวกับคอลัมน์ที่ส่งคืนจากแบบสอบถามเฉพาะอย่างง่ายดาย ฉันใช้สิ่งต่อไปนี้เพื่อพิจารณาการเปรียบเทียบที่ไม่ตรงกัน:

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;

ผลลัพธ์ที่ได้:

ป้อนคำอธิบายรูปภาพที่นี่

เหตุใดจึงมีการจัดเรียงคอลัมน์เหล่านี้แบบคงที่

คำตอบ:


17

คำที่เป็นทางการจาก Microsoft:

คอลัมน์บางส่วนที่มีสตริงที่กำหนดไว้ล่วงหน้า (เช่นประเภทคำอธิบายระบบและค่าคงที่) จะได้รับการแก้ไขในการเปรียบเทียบที่เฉพาะเจาะจงLatin1_General_CI_AS_KS_WSเสมอ- นี่คือโดยไม่คำนึงถึงการเปรียบเทียบอินสแตนซ์ / ฐานข้อมูล เหตุผลก็คือนี่เป็นข้อมูลเมตาของระบบ (ไม่ใช่ข้อมูลเมตาของผู้ใช้) และโดยทั่วไปสตริงเหล่านี้จะถือว่าเป็นแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ (เช่นคำหลักดังนั้นละตินเสมอ)

คอลัมน์อื่น ๆ ในตารางระบบที่มีข้อมูลเมตาของผู้ใช้เช่นชื่อวัตถุชื่อคอลัมน์ชื่อดัชนีชื่อล็อกอิน ฯลฯ ใช้อินสแตนซ์หรือการเปรียบเทียบฐานข้อมูล คอลัมน์จะถูกเรียงตามการจัดเรียงที่เหมาะสม ณ เวลาที่ติดตั้ง SQL Server ในกรณีที่มีการเรียงอินสแตนซ์ & ในเวลาที่สร้างฐานข้อมูลในกรณีที่มีการเรียงฐานข้อมูล

คุณถาม (เน้นที่เหมือง):

เหตุใดจึงมีการจัดเรียงคอลัมน์เหล่านี้แบบคงที่

เหตุผลบางคอลัมน์ถูกตั้งค่าแบบสแตติกคือเพื่อให้แบบสอบถามไม่จำเป็นต้องกังวลเกี่ยวกับการจัดเรียงเซิร์ฟเวอร์หรือฐานข้อมูล (ที่สำคัญกว่า: CaSe SenSiTIviTy) เพื่อให้ทำงานได้อย่างถูกต้อง แบบสอบถามนี้จะทำงานเสมอโดยไม่คำนึงถึงการเปรียบเทียบ:

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

ในกรณีที่การเปรียบเทียบเซิร์ฟเวอร์เป็นแบบตรงตัวพิมพ์เล็ก - ใหญ่แบบสอบถามข้างต้นจะคืนค่า 0 แถวเช่นนี้:

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

ตัวอย่างเช่นถ้าคุณติดตั้งอินสแตนซ์ของ SQL Server ด้วยการSQL_Estonian_CP1257_CS_ASเปรียบเทียบแล้วเรียกใช้ต่อไปนี้:

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

คุณจะเห็นผลลัพธ์เหล่านี้ (หรือคล้ายกันขึ้นอยู่กับรุ่นของ 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

ในตอนนี้เพื่อแสดงมุมมองเมทาดาทาที่รับช่วงการรวบรวมฐานข้อมูลแทนที่จะสืบทอดการจัดเรียงเซิร์ฟเวอร์จากฐานข้อมูลหลัก:

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

ผล:

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

ดังนั้นคุณจะเห็นได้ว่าในกรณีนี้หลายคอลัมน์สืบทอดการจัดเรียงฐานข้อมูลในขณะที่คอลัมน์อื่น ๆ ได้รับการแก้ไขในการจัดเรียง "Latin1" ทั่วไป "ซึ่งหมายความว่ามันถูกใช้เพื่อป้องกันชื่อและคุณสมบัติบางอย่าง

หากคุณพยายามที่จะทำUNIONตัวอย่างเช่น:

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

คุณได้รับข้อผิดพลาดนี้:

ข่าวสารเกี่ยวกับ 451 ระดับ 16 สถานะ 1
ไม่สามารถแก้ไขข้อขัดแย้งการเรียงระหว่าง "Albanian_BIN" และ "SQL_Estonian_CP1257_CS_AS" ในตัวดำเนินการ UNION ทั้งหมดที่เกิดขึ้นในคอลัมน์คำสั่ง SELECT 1

ในทำนองเดียวกันถ้าคุณพยายามที่จะดำเนินการPIVOTหรือUNPIVOTกฎระเบียบที่มีแม้กระทั่งเข้มงวด (ประเภทการส่งออกจะต้องแข่งขันทั้งหมดตรงมากกว่าแค่จะเข้ากันได้) แต่ข้อผิดพลาดอยู่ไกลที่เป็นประโยชน์น้อยและแม้กระทั่งความเข้าใจผิด:

เกี่ยวกับข่าวสาร 8167 ระดับ 16 สถานะ 1
ชนิดของคอลัมน์ "ชื่อคอลัมน์" ขัดแย้งกับชนิดของคอลัมน์อื่น ๆ ที่ระบุในรายการ UNPIVOT

คุณต้องแก้ไขข้อผิดพลาดเหล่านี้โดยใช้COLLATEคำสั่งที่ชัดเจนในการสืบค้นของคุณ ตัวอย่างเช่นสหภาพข้างบนอาจเป็น:

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;

ในครั้งเดียวที่อาจทำให้เกิดปัญหานี้คือคุณจะได้ผลลัพธ์ที่สับสนหากการเรียงถูกบังคับ แต่ไม่มีการแสดงอักขระเดียวกันหรือหากมีการใช้การเรียงลำดับและการเรียงแบบบังคับจะใช้ลำดับการเรียงที่แตกต่างจากแหล่งที่มา


7

พื้นหลังในการเรียงลำดับความสำคัญ

พฤติกรรมที่คุณเห็นเกี่ยวกับการเรียงชุดข้อมูลของฟิลด์ต่าง ๆ ในมุมมองแคตตาล็อกระบบเป็นผลมาจากวิธีการกำหนดแต่ละฟิลด์และลำดับความสำคัญการเรียงลำดับ

เมื่อดูที่sys.databasesสิ่งสำคัญคือต้องจำไว้ว่าไม่ใช่ตาราง ในขณะที่ในอดีตที่ผ่านมา (ผมคิดว่าตอนจบที่ SQL Server 2000) เหล่านี้เป็นระบบแคตตาล็อกตารางตอนนี้พวกเขาแคตตาล็อกระบบมุมมอง ดังนั้นแหล่งที่มาของข้อมูลในนั้นไม่จำเป็นต้องมาจากบริบทฐานข้อมูลปัจจุบัน (หรือบริบทของฐานข้อมูลที่ระบุเมื่อจัดการกับวัตถุที่ผ่านการรับรองอย่างเช่นmaster.sys.databases)

การจัดการโดยเฉพาะกับsys.databasesบางฟิลด์มาจาก[master]ฐานข้อมูล (ซึ่งถูกสร้างขึ้นด้วยการเปรียบเทียบตามการเปรียบเทียบเริ่มต้นของอินสแตนซ์ - เช่นการเปรียบเทียบระดับเซิร์ฟเวอร์) บางฟิลด์เป็นนิพจน์ (เช่นCASEคำสั่ง) และบางอันกำลังมา จากแหล่ง "ซ่อน": [mssqlsystemresource]ฐานข้อมูล และฐานข้อมูลมีการเปรียบเทียบของ:[mssqlsystemresource]Latin1_General_CI_AS_KS_WS

nameฟิลด์มีที่มาจากภาคสนามname master.sys.sysdbregดังนั้นฟิลด์นี้ควรอยู่ในการเปรียบเทียบของ[master]ฐานข้อมูลซึ่งจะตรงกับการเปรียบเทียบของเซิร์ฟเวอร์อีกครั้ง

แต่ฟิลด์ต่อไปนี้sys.databasesมาจาก[name]ฟิลด์ใน[mssqlsystemresource].[sys].[syspalvalues]:

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

Latin1_General_CI_AS_KS_WSเขตข้อมูลเหล่านั้นควรมีการตรวจทานของ

อย่างไรก็ตามcollation_nameฟิลด์มาจากนิพจน์ต่อไปนี้:

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)

นี่คือที่เปรียบเทียบลำดับความสำคัญเริ่มต้นที่จะมาในตัวเลือกทั้งสองสำหรับการส่งออกที่นี่มีฟังก์ชั่นระบบ:. และserverproperty() collationpropertyfromid()การเปรียบเทียบของนิพจน์นี้ถือว่าเป็น "Coercible-default":

ตัวแปรสตริงอักขระ Transact-SQL พารามิเตอร์ตัวอักษรหรือเอาต์พุตของฟังก์ชันในตัวแค็ตตาล็อกหรือฟังก์ชันในตัวที่ไม่รับอินพุตสตริง แต่สร้างเอาต์พุตสตริง

ถ้าวัตถุถูกประกาศในฟังก์ชั่นที่ผู้ใช้กำหนดกระบวนงานที่เก็บไว้หรือทริกเกอร์วัตถุนั้นจะถูกกำหนดให้มีการเรียงข้อมูลเริ่มต้นของฐานข้อมูลที่สร้างฟังก์ชันกระบวนงานที่เก็บไว้หรือทริกเกอร์ หากมีการประกาศวัตถุในแบตช์วัตถุจะได้รับการจัดเรียงเริ่มต้นของฐานข้อมูลปัจจุบันสำหรับการเชื่อมต่อ

ในแง่ของย่อหน้าที่ 2 เนื่องจากsys.databasesเป็นมุมมองที่มีอยู่ในmasterฐานข้อมูลจะใช้ในการเปรียบเทียบของmasterฐานข้อมูล (ไม่ใช่ฐานข้อมูลปัจจุบัน)

state_descฟิลด์ยังคือการแสดงออก:

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

Latin1_General_CI_AS_KS_WSแต่การเปรียบเทียบการแสดงออกนี้ ทำไม? มีการแนะนำสิ่งใหม่ในนิพจน์นี้: การอ้างอิงถึงฟิลด์จริง: [mssqlsystemresource].[sys].[syspalvalues].[name]ในELSEประโยคสุดท้าย การอ้างอิงคอลัมน์ถือว่าเป็น "นัย":

การอ้างอิงคอลัมน์ การเปรียบเทียบของนิพจน์นั้นนำมาจากการเรียงที่กำหนดไว้สำหรับคอลัมน์ในตารางหรือมุมมอง

แน่นอนว่านี่เป็นการเปิดคำถามที่น่าสนใจ: เป็นไปได้หรือไม่ที่นิพจน์นี้จะส่งคืนการเปรียบเทียบที่แตกต่างกันขึ้นอยู่กับวิธีการCASEประเมิน ตัวอักษรจะอยู่ในการเรียงของฐานข้อมูลที่วัตถุนี้ถูกกำหนดไว้ แต่ELSEเงื่อนไขจะส่งกลับค่าของเขตข้อมูลที่ควรเก็บรักษาการเปรียบเทียบเดิม โชคดีที่เราสามารถจำลองการทดสอบโดยใช้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

คืนค่า (บนอินสแตนซ์ที่ตั้งค่าด้วยการเรียงหน้าSQL_Latin1_General_CP1_CI_ASแต่ทำงานในฐานข้อมูลที่มีการเรียงหน้า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

ที่นี่เราเห็นว่าทั้งสองแบบสอบถามที่อ้างอิงเขตข้อมูลใน[msdb]การเปรียบเทียบของ[msdb]ฐานข้อมูล (ซึ่งเป็นระบบฐานข้อมูลถูกกำหนดโดยการเปรียบเทียบเซิร์ฟเวอร์)

เกี่ยวข้องกับคำถามเดิม

พฤติกรรมที่สังเกตได้คือฟิลด์ต่อไปนี้ไม่ได้สังเกตว่าการเปรียบเทียบเซิร์ฟเวอร์หรือการเปรียบเทียบฐานข้อมูล พวกเขาจะถูกนำเสนอเสมอในการLatin1_General_CI_AS_KS_WSเปรียบเทียบ

การสังเกตของคุณเป็นแบบ Spot-on: เขตข้อมูลเหล่านั้นจะมีการเปรียบเทียบเสมอLatin1_General_CI_AS_KS_WSโดยไม่คำนึงถึงการเปรียบเทียบเซิร์ฟเวอร์หรือการเปรียบเทียบฐานข้อมูล และเหตุผลคือ: ลำดับความสำคัญการเรียง เขตข้อมูลเหล่านั้นมาจากตารางใน[mssqlsystemresource]ฐานข้อมูลและจะเก็บรักษาการเปรียบเทียบเริ่มต้นนั้นไว้เว้นแต่จะถูกแทนที่ด้วยCOLLATEประโยคที่ชัดเจนเนื่องจากมีลำดับความสำคัญสูงสุด:

Explicit = นิพจน์ที่ส่งไปยังการจัดเรียงเฉพาะอย่างชัดเจนโดยใช้ส่วนคำสั่ง COLLATE ในนิพจน์

ชัดเจนจะมีความสำคัญมากกว่าโดยปริยาย โดยนัยมีความสำคัญมากกว่า Coercible-default:
Explicit> Implicit> Coercible-default

และคำถามที่เกี่ยวข้อง:

เหตุใดจึงมีการจัดเรียงคอลัมน์เหล่านี้แบบคงที่

ไม่ใช่ว่ามันถูกตั้งค่าแบบสแตติกหรือฟิลด์อื่นจะเป็นแบบไดนามิก ฟิลด์ทั้งหมดในมุมมองแค็ตตาล็อกระบบทั้งหมดนั้นทำงานบนกฎการเรียงลำดับความสำคัญแบบเดียวกัน เหตุผลที่ปรากฏเป็น "คงที่" มากกว่าเขตข้อมูลอื่น (เช่นจะไม่เปลี่ยนแปลงแม้ว่าคุณจะติดตั้ง SQL Server ด้วยการเปรียบเทียบค่าเริ่มต้นที่แตกต่างกันซึ่งจะสร้างฐานข้อมูลระบบด้วยการเปรียบเทียบค่าเริ่มต้น) นั่นคือ[mssqlsystemresource]ฐานข้อมูลอย่างสม่ำเสมอ มีการเปรียบเทียบLatin1_General_CI_AS_KS_WSระหว่างการติดตั้ง SQL Server ใด ๆ (หรือดังนั้นจะปรากฏขึ้นอย่างแน่นอน) และนี่เป็นเหตุผลเพราะมิฉะนั้น SQL Server จะจัดการตัวเองภายในได้ยาก (เช่นถ้ากฎการเรียงลำดับและการเปรียบเทียบที่ใช้สำหรับลอจิกภายในเปลี่ยนไปตามการติดตั้ง)

วิธีการดูเฉพาะเหล่านี้ด้วยตัวคุณเอง

หากคุณต้องการดูแหล่งที่มาของฟิลด์ใด ๆ ในมุมมองแคตตาล็อกระบบเหล่านี้ให้ทำดังต่อไปนี้:

  1. ในแท็บแบบสอบถามใน SSMS เปิดใช้งานตัวเลือกข้อความค้นหาของ "รวมแผนการดำเนินการตามจริง" ( CTRL-M)
  2. ดำเนินการแบบสอบถามเลือกหนึ่งเขตข้อมูลจากหนึ่งในมุมมองแคตตาล็อกระบบ (ฉันขอแนะนำให้เลือกเพียงหนึ่งครั้งในช่วงตั้งแต่แผนปฏิบัติการมีขนาดใหญ่ / ซับซ้อนขันสำหรับแม้แต่เพียงหนึ่งเขตข้อมูลและจะรวมถึงการอ้างอิงไปยังหลายสาขาที่คุณไม่ได้ ไม่เลือก):

    SELECT recovery_model_desc FROM sys.databases;
  3. ไปที่แท็บ "แผนการดำเนินการ"
  4. คลิกขวาในพื้นที่แผนการดำเนินการกราฟิกและเลือก "แสดงแผนปฏิบัติการ XML ... "
  5. แท็บใหม่ใน SSMS จะเปิดขึ้นพร้อมชื่อคล้ายกับ: Execution plan.xml
  6. ไปที่Execution plan.xmlแท็บ
  7. ค้นหาการเกิดขึ้นครั้งแรกของ<OutputList>แท็ก (ควรอยู่ระหว่างบรรทัด 10 และ 20 โดยปกติ)
  8. ควรมี<ColumnReference>แท็ก คุณลักษณะในแท็กนั้นควรชี้ไปที่เขตข้อมูลเฉพาะในตารางหรือชี้ไปที่นิพจน์ที่กำหนดไว้ในภายหลังในแผน
  9. หากแอ็ตทริบิวต์ชี้ไปที่ฟิลด์จริงแสดงว่าคุณทำเสร็จแล้วเนื่องจากมีข้อมูลทั้งหมด ต่อไปนี้คือสิ่งที่ปรากฏสำหรับrecovery_model_descฟิลด์:

    <ColumnReference Database="[mssqlsystemresource]" Schema="[sys]"
                     Table="[syspalvalues]" Alias="[ro]" Column="name" />
  10. หากแอตทริบิวต์ชี้ไปที่นิพจน์เช่นหากคุณเลือกstate_descเขตข้อมูลจากนั้นคุณจะพบว่า:

    <ColumnReference Column="Expr1024" />
  11. ในกรณีนี้คุณต้องตรวจสอบส่วนที่เหลือของแผนเพื่อหาคำจำกัดความExpr1024หรือสิ่งที่ # เกิดขึ้น โปรดทราบว่าอาจมีการอ้างอิงเหล่านี้หลายรายการ แต่คำจำกัดความจะไม่อยู่ใน<OutputList>บล็อก อย่างไรก็ตามมันจะมี<ScalarOperator>องค์ประกอบพี่น้องที่มีความหมาย ต่อไปนี้คือสิ่งที่ปรากฏสำหรับstate_descฟิลด์:

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

สามารถทำเช่นเดียวกันเพื่อตรวจสอบแหล่งที่มาของมุมมองแคตตาล็อกระดับฐานข้อมูลได้เช่นกัน การทำเช่นนี้เพื่อวัตถุsys.tablesจะแสดงให้เห็นว่าnameเขตข้อมูลนั้นมาจาก[current_db].[sys].[sysschobjs](ซึ่งเป็นสาเหตุที่มันมีการเปรียบเทียบที่สอดคล้องกับการเปรียบเทียบของฐานข้อมูล) ในขณะที่lock_escalation_descสนามมาจาก[mssqlsystemresource].[sys].[syspalvalues](ซึ่งเป็นเหตุผลที่มันมีการเปรียบเทียบLatin1_General_CI_AS_KS_WS)

Clippy กล่าวว่า "ดูเหมือนว่าคุณต้องการทำแบบสอบถาม UNPIVOT"

ตอนนี้เรารู้แล้วว่าเหตุใดการลำดับความสำคัญของการจัดเรียงคืออะไรและทำงานอย่างไรให้ใช้ความรู้นั้นกับเคียวรี UNPIVOT

สำหรับUNPIVOTการดำเนินงาน SQL Server ดูเหมือนว่าจริงๆต้องการ (หมายถึง: ต้อง) ที่สนามแต่ละแหล่งจะเป็นของประเภทเดียวกันแน่นอน โดยปกติแล้ว "type" หมายถึงประเภทฐาน (เช่นVARCHAR/ NVARCHAR/ INT/ ฯลฯ ) แต่ที่นี่ SQL Server ยังรวมถึงการ COLLATION สิ่งนี้ไม่ควรถูกมองว่าไม่มีเหตุผลเนื่องจากการควบคุม Collations: ชุดอักขระ (เช่นรหัสเพจ) สำหรับ VARCHAR และกฎทางภาษาที่กำหนดความเท่าเทียมกันของตัวละครและชุดอักขระ (เช่นการทำให้เป็นมาตรฐาน) ต่อไปนี้เป็น mimi-primer สำหรับ Unicode "normalization" คืออะไร:

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

ผลตอบแทน:

---
Danish_Greenlandic_100_CI_AI
---
Upper_Sorbian_100_CI_AI
---

ตอนนี้เรามาเริ่มแบบสอบถามที่เป็นต้นฉบับของคุณกัน เราจะทำการทดสอบสองสามครั้งเพื่อดูว่าการเปลี่ยนแปลงต่างๆเปลี่ยนแปลงผลลัพธ์อย่างไรและเราจะดูว่าการเปลี่ยนแปลงเพียงเล็กน้อยสามารถแก้ไขได้

  1. ข้อผิดพลาดแรกคือเกี่ยวกับCompatibilityLevelฟิลด์ซึ่งเป็นฟิลด์ที่สองที่จะไม่ถูกตรึงและเพิ่งเกิดขึ้นเป็นนิพจน์ที่มีตัวอักษรสตริงทั้งหมด หากไม่มีการอ้างอิงฟิลด์ใด ๆ การเปรียบเทียบที่เกิดขึ้นจะถือว่าเป็น "การบังคับใช้ค่าเริ่มต้น") SQL_Latin1_General_CP1_CI_ASCoercible-ค่าเริ่มต้นจะใช้ในการเปรียบเทียบของฐานข้อมูลปัจจุบันสมมติว่า ฟิลด์ถัดไป 20 ฟิลด์นั้นเป็นเพียงตัวอักษรสตริงเท่านั้นและด้วยเหตุนี้จึงเป็นค่าเริ่มต้นที่บังคับได้ดังนั้นจึงไม่ควรขัดแย้งกัน แต่ถ้าเรามองกลับไปที่สนามแรกrecovery_model_descนั่นคือมาโดยตรงจากสนามในsys.databasesซึ่งทำให้การเรียง "โดยนัย" และสิ่งนี้ไม่ได้ใช้ในการเปรียบเทียบของฐานข้อมูลท้องถิ่น แต่แทนที่จะรักษาเดิมการเรียงซึ่งเป็นLatin1_General_CI_AS_KS_WS( เพราะมันมาจาก[mssqlsystemresource]ฐานข้อมูลจริงๆ)

    ดังนั้นหากฟิลด์ 1 (RecoveryModel) เป็นLatin1_General_CI_AS_KS_WSและฟิลด์ 2 (CompatibilityLevel) คือSQL_Latin1_General_CP1_CI_ASเราควรจะบังคับให้ฟิลด์ 2 Latin1_General_CI_AS_KS_WSตรงกับฟิลด์ 1 จากนั้นข้อผิดพลาดควรปรากฏสำหรับฟิลด์ 3 (AutoClose)

    เพิ่มรายการต่อไปนี้ที่ท้ายCompatibilityLevelบรรทัด:
    COLLATE Latin1_General_CI_AS_KS_WS

    แล้วเรียกใช้แบบสอบถาม ตอนนี้ข้อผิดพลาดระบุว่าเป็นAutoCloseเขตข้อมูลที่มีข้อขัดแย้ง

  2. สำหรับการทดสอบครั้งที่สองของเราเราจำเป็นต้องยกเลิกการเปลี่ยนแปลงที่เราเพิ่งทำ (เช่นลบCOLLATEข้อจากปลายCompatibilityLevelบรรทัด

    ตอนนี้ถ้า SQL Server กำลังประเมินตามลำดับที่ระบุฟิลด์อย่างแท้จริงเราควรจะสามารถลบฟิลด์ 1 (RecoveryModel) ซึ่งจะทำให้ฟิลด์ปัจจุบัน 2 (CompatibilityLevel) เป็นฟิลด์ที่ตั้งค่าการเปรียบเทียบหลักของ ผลลัพธ์ UNPIVOT และCompatibilityLevelเขตข้อมูลนั้นเป็นค่าเริ่มต้น coercible ซึ่งเกิดขึ้นในการเปรียบเทียบฐานข้อมูลดังนั้นข้อผิดพลาดแรกควรเป็นPageVerifyเขตข้อมูลซึ่งเป็นการอ้างอิงภาคสนามซึ่งเป็นการเปรียบเทียบแบบปริยายซึ่งเป็นการเปรียบเทียบแบบดั้งเดิมซึ่งคงอยู่ในการเปรียบเทียบแบบเดิมซึ่งในกรณีLatin1_General_CI_AS_KS_WSนี้ไม่ใช่ การเรียงของฐานข้อมูลปัจจุบัน

    ดังนั้นไปข้างหน้าและแสดงความคิดเห็นบรรทัดที่ขึ้นต้นด้วย, RecoveryModelในSELECT(ไปทางด้านบน) จากนั้นใส่เครื่องหมายRecoveryModelบรรทัดในUNPIVOTประโยคด้านล่างและลบเครื่องหมายจุลภาคนำออกจากบรรทัดต่อไปนี้เพื่อCompatibilityLevelให้คุณไม่ได้รับข้อผิดพลาดทางไวยากรณ์

    เรียกใช้แบบสอบถามนั้น ตอนนี้ข้อผิดพลาดระบุว่าเป็นPageVerifyเขตข้อมูลที่มีข้อขัดแย้ง

  3. สำหรับการทดสอบครั้งที่สามของเราเราจำเป็นต้องยกเลิกการเปลี่ยนแปลงที่เราเพิ่งทำเพื่อลบRecoveryModelสนาม ดังนั้นไปข้างหน้าและนำเครื่องหมายจุลภาคกลับมาและยกเลิกการใส่เครื่องหมายความคิดเห็นสองบรรทัดอื่น ๆ

    ตอนนี้เราสามารถไปอีกทิศทางหนึ่งโดยบังคับให้มีการเปรียบเทียบ แทนที่จะเปลี่ยนการเรียงของเขตข้อมูลเปรียบเทียบ coercible- เริ่มต้น (ซึ่งส่วนใหญ่ของพวกเขา) เราควรจะสามารถเปลี่ยนเขตข้อมูลเปรียบเทียบโดยนัยไปที่ของฐานข้อมูลปัจจุบันใช่ไหม?

    ดังนั้นเหมือนกับการทดสอบครั้งแรกของเราเราควรจะสามารถบังคับให้มีการเรียงของฟิลด์ 1 (RecoveryModel) ด้วยCOLLATEประโยคที่ชัดเจน แต่ถ้าเราระบุการเปรียบเทียบที่เฉพาะเจาะจงแล้วเรียกใช้แบบสอบถามในฐานข้อมูลที่มีการเปรียบเทียบที่แตกต่างกันเขตข้อมูลการเปรียบเทียบที่เริ่มต้น coercible จะรับการเปรียบเทียบใหม่ซึ่งจะขัดแย้งกับสิ่งที่เรากำลังตั้งค่าเขตข้อมูลแรกนี้ ดูเหมือนว่าจะเจ็บปวด โชคดีที่มีวิธีจัดการกับสิ่งนี้แบบไดนามิก มีการจัดเรียงหลอกเรียกDATABASE_DEFAULTว่ารับการเปรียบเทียบฐานข้อมูลปัจจุบัน (เช่นเดียวกับเขตข้อมูลบังคับเริ่มต้นทำ)

    ไปข้างหน้าและเพิ่มสิ่งต่อไปนี้ที่ส่วนท้ายของบรรทัดไปด้านบนซึ่งเริ่มต้นด้วย, RecoveryModel: COLLATE DATABASE_DEFAULT

    เรียกใช้แบบสอบถามนั้น นั่นเองข้อผิดพลาดระบุอีกครั้งว่ามันเป็นPageVerifyเขตข้อมูลที่มีความขัดแย้ง

  4. สำหรับการทดสอบขั้นสุดท้ายเราไม่จำเป็นต้องยกเลิกการเปลี่ยนแปลงใด ๆ ก่อนหน้านี้

    ทั้งหมดที่เราต้องทำตอนนี้เพื่อแก้ไขปัญหานี้UNPIVOTแบบสอบถามคือการเพิ่มCOLLATE DATABASE_DEFAULTการสิ้นสุดของที่เหลือเขตเรียงนัย: และPageVerify RestrictedAccessในขณะที่Collationเขตข้อมูลยังมีการเปรียบเทียบโดยนัยเขตข้อมูลนั้นมาจากmasterฐานข้อมูลซึ่งโดยทั่วไปจะสอดคล้องกับฐานข้อมูล "ปัจจุบัน" แต่ถ้าคุณต้องการความปลอดภัยเพื่อที่ว่าสิ่งนี้จะได้ผลเสมอไปให้เพิ่มCOLLATE DATABASE_DEFAULTส่วนท้ายของฟิลด์นั้นด้วย

    เรียกใช้แบบสอบถามนั้น แน่นอนว่าไม่มีข้อผิดพลาด ทั้งหมดที่ใช้ในการแก้ไขแบบสอบถามนี้มีการเพิ่มCOLLATE DATABASE_DEFAULTที่ส่วนท้ายของ 3 ฟิลด์ (จำเป็น) และอาจจะมากกว่า 1 รายการ (ไม่บังคับ)

  5. ตัวเลือกการทดสอบ: ตอนนี้เราก็มีการสอบถาม Unpivot ทำงานอย่างถูกต้องเปลี่ยนแปลงเพียงหนึ่งในคำจำกัดความใด ๆ ของสนามเริ่มต้นด้วยCONVERT(VARCHAR(50),แทนที่จะเป็นเช่น:51CONVERT(VARCHAR(51),

    เรียกใช้แบบสอบถาม คุณควรได้รับThe type of column "X" conflicts with the type of other columns specified in the UNPIVOT list.ข้อผิดพลาดเดียวกับที่คุณได้รับเมื่อเปรียบเทียบเฉพาะที่ไม่ตรงกัน

    การได้รับข้อผิดพลาดเดียวกันสำหรับทั้งประเภทข้อมูลและการเปรียบเทียบไม่ตรงกันนั้นไม่เพียงพอที่จะเป็นประโยชน์ ดังนั้นจึงมีห้องพักสำหรับการปรับปรุงที่แน่นอน :)


หมายเหตุที่เกี่ยวข้องกับแบบสอบถามมากกว่าคำถามเฉพาะเกี่ยวกับการเรียงหน้า:

ตั้งแต่ฟิลด์ทั้งหมดที่มาเป็นประเภทข้อมูลNVARCHARก็จะปลอดภัยในCONVERTเขตการส่งออกทั้งหมดไปแทนNVARCHAR VARCHARคุณอาจไม่ได้จัดการกับข้อมูลในขณะนี้ที่มีอักขระที่ไม่ได้มาตรฐาน ASCII แต่ meta-data ของระบบอนุญาตให้มีการแปลงให้NVARCHAR(128)เป็นความยาวสูงสุดที่ใหญ่ที่สุดของฟิลด์เหล่านั้น - อย่างน้อยที่สุด รับประกันได้ว่าจะไม่มีปัญหาสำหรับคุณในอนาคตหรือสำหรับใครก็ตามที่คัดลอกรหัสนี้ที่อาจมีตัวละครเหล่านั้นบางส่วนในระบบของพวกเขา


5

นี่เป็นวิธีแก้ปัญหาสำหรับปัญหาเฉพาะแทนที่จะตอบเต็มคำถาม คุณสามารถหลีกเลี่ยงข้อผิดพลาดได้โดยเปลี่ยนsql_variantเป็น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;

ฉันได้เพิ่มสามคอลัมน์สำหรับข้อมูลเกี่ยวกับประเภทพื้นฐานของOptionValueคอลัมน์

ตัวอย่างผลลัพธ์

หากลูกค้าไม่สามารถจัดการกับsql_variantข้อมูลที่ทำครั้งสุดท้าย (ระดับบนสุด) แปลงในคอลัมน์ไปเช่นunpvt.OptionValuenvarchar(256)


4

ตกลงดังนั้นฉันจึงดู

sp_helptext [sys.databases]

จากนั้นพังลงมาที่คอลัมน์มาจาก สิ่งที่มีการLatin1_General_CI_AS_KS_WSเปรียบเทียบทั้งหมดมาจากตารางระบบsys.syspalvaluesซึ่งดูเหมือนจะเป็นตารางการค้นหาทั่วไป (เป็นตารางระบบดังนั้นคุณจะต้องเชื่อมต่อผ่าน DAC เพื่อดู)

ฉันเดาว่ามันถูกตั้งค่าLatin1_General_CI_AS_KS_WSให้จัดการค่าการค้นหาใด ๆ ที่เป็นไปได้ ฉันสามารถดูว่ามันจะน่ารำคาญแม้ว่า

อีกวิธีในการดูคำจำกัดความ (แต่เดิมจัดทำโดยMaxในความคิดเห็น) คือ:

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';`
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.