SQL 服务器 - UNPIVOT 期间列类型冲突

SQL Server - Conflicts column type during UNPIVOT

需要一些帮助。我正在创建 SQL 服务器单元测试来检查电子邮件服务器配置,并且在使用 UNPIVOT 时我有一个奇怪的行为。以下代码在供应商数据库上运行时会抛出错误,但在默认数据库(master、model、msdb、tempdb)中运行良好。有什么想法吗?

Msg 8167, Level 16, State 1, Line 16 The type of column "port" conflicts with the type of other columns specified in the UNPIVOT list.

        SELECT * FROM (
                SELECT 
                CAST(p.name AS VARCHAR(256)) name, 
                CAST(a.email_address AS VARCHAR(256)) email_address, 
                CAST(a.display_name AS VARCHAR(256)) display_name, 
                CAST(a.replyto_address AS VARCHAR(256)) replyto_address,
                CAST(s.servertype AS VARCHAR(256)) servertype,  
                CAST(s.servername AS VARCHAR(256)) servername,
                CAST(s.port AS VARCHAR(256)) port
                FROM msdb.dbo.sysmail_profile p 
                JOIN msdb.dbo.sysmail_profileaccount pa ON p.profile_id = pa.profile_id 
                JOIN msdb.dbo.sysmail_account a ON pa.account_id = a.account_id 
                JOIN msdb.dbo.sysmail_server s ON a.account_id = s.account_id
            ) AS dummyName1
            UNPIVOT(
                configValue for configKey IN (name, email_address, display_name, replyto_address, servertype, servername, port)
            )   as dummyName2

基于它适用于某些数据库而不适用于其他数据库的事实,最有可能的罪魁祸首是排序规则。

当您将 varchar/nvarchar 列转换为 varchar/nvarchar 时,它将保持其现有排序规则。当您转换另一种类型如 int(如端口列)时,它采用您正在执行查询的数据库的默认排序规则。如果供应商数据库的排序规则与 msdb 不同,则 unpivot 将给出您所看到的错误。

几个可能的修复:

  1. 在你的查询之前添加这个以强制它从 msdb 执行:

    USE msdb;
    
  2. 手动强制您的端口列使用与 msdb 相同的排序规则:

    --Check the collation for msdb
    SELECT name,collation_name FROM master.sys.databases
    --Use that collation (ex: SQL_Latin1_General_CP1_CI_AS) in your query
    CAST(s.port AS VARCHAR(256)) COLLATE SQL_Latin1_General_CP1_CI_AS port
    
  3. 强制其他列使用与端口相同的默认排序规则:

    CAST(p.name AS VARCHAR(256)) COLLATE DATABASE_DEFAULT name, 
    CAST(a.email_address AS VARCHAR(256)) COLLATE DATABASE_DEFAULT email_address,
    ...
    

我还没有尝试过 Larnu 解决方案,但是 bt224 提到的整理解决了这个问题。默认数据库使用 SQL_Latin1_General_CP1_CI_AS,而供应商数据库使用 Latin1_General_CI_AS。 这是更新后的代码,它可以工作!谢谢 bt224!

SELECT * FROM (
                SELECT 
                CAST(p.name AS VARCHAR(256)) COLLATE DATABASE_DEFAULT name, 
                CAST(a.email_address AS VARCHAR(256)) COLLATE DATABASE_DEFAULT email_address, 
                CAST(a.display_name AS VARCHAR(256)) COLLATE DATABASE_DEFAULT display_name, 
                CAST(a.replyto_address AS VARCHAR(256)) COLLATE DATABASE_DEFAULT replyto_address,
                CAST(s.servertype AS VARCHAR(256)) COLLATE DATABASE_DEFAULT servertype,  
                CAST(s.servername AS VARCHAR(256)) COLLATE DATABASE_DEFAULT servername,
                CAST(s.port AS VARCHAR(256)) COLLATE DATABASE_DEFAULT port
                FROM msdb.dbo.sysmail_profile p 
                JOIN msdb.dbo.sysmail_profileaccount pa ON p.profile_id = pa.profile_id 
                JOIN msdb.dbo.sysmail_account a ON pa.account_id = a.account_id 
                JOIN msdb.dbo.sysmail_server s ON a.account_id = s.account_id
            ) AS dummyName1
            UNPIVOT(
                configValue for configKey IN (name, email_address, display_name, replyto_address, servertype, servername, port)
            )   as dummyName2