创建存储过程时排序规则冲突

Mar*_*dar 3 sql-server collation json sql-server-2019

我正在尝试创建一个存储过程,但它给了我错误消息
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_BIN2" in the EXCEPT operation.

问题是数据库和服务器排序规则都是SQL_Latin1_General_CP1_CI_AS,但我不知道Latin1_General_BIN2排序规则来自哪里。

create procedure [ETL_1.4.0].update_valve_event_type
(
    @data nvarchar(max)
)
as
    declare @mismatch_table table(id int, [name] varchar(50))

    if isjson(@data) = 0
    begin
        ;throw 50000,'Input argument @data is invalid JSON.', 1
    end

    insert @mismatch_table (id, [name])
    select * from
    (
        select
            value as id,
            [key] as [name]
        from openjson(@data)
        except
        select
            id,
            [name]
        from enum.valve_event
    )data

    --clear mismatches that are deemed ok, e.g. spelling corrections
    delete from @mismatch_table
    where id = 10 and [name] = 'FEEDBACK_FROM_USER'

    if exists(select * from @mismatch_table)
    begin
        ;throw 50000,'Terminal version of enum for valve events does not match the EDW version', 1
    end
go
Run Code Online (Sandbox Code Playgroud)

那么有人可以解释我缺少什么吗?
如果重要的话,这个数据库(以及与此相关的服务器)还没有上线,所以从技术上讲,我有能力在任何我想要的地方更改排序规则。

Sol*_*zky 6

它可能是函数的输出OPENJSON,特别是因为它是操作的一部分EXCEPT。您需要做的就是通过COLLATE选项/关键字在该查询中强制进行排序规则。例如:

...
    [key] COLLATE SQL_Latin1_General_CP1_CI_AS as [name]
from openjson(@data)
...
Run Code Online (Sandbox Code Playgroud)

至于强制使用哪种排序规则,请选择一个支持您希望操作如何表现的排序规则(简单来说:应该'A' = 'a' 'A' <> 'a')。两个最可能的选项是已使用的两种排序规则:Latin1_General_BIN2, 和 (在本例中)SQL_Latin1_General_CP1_CI_AS。(是的,如果您选择Latin1_General_BIN2的已经是列的排序规则key,您仍然需要指定该COLLATE选项,如上所示,以避免错误。)

OPENJSON()的文档甚至松散地声明输出使用BIN2排序规则(尽管它没有指定确切的排序规则,而且我不会引用它,因为描述不完全正确)。

最好的选择是直接询问 SQL Server。我们可以通过将输出列存储在 SQL Server 动态创建的表中(通过SELECT INTO ...)来验证输出列的排序规则,然后检查新创建的表中列的属性:

SELECT *
INTO #CheckCollation
FROM OPENJSON(N'{}'));

SELECT [name], [system_type_id], [collation_name], [is_nullable]
FROM   tempdb.sys.columns col
WHERE  col.[object_id] = OBJECT_ID(N'tempdb..#CheckCollation');

-- [key] column has collation of Latin1_General_BIN2
-- [value] column has collation of passed-in JSON value:
--     if passing in column, then collation of column, OR
--     if passing in variable or literal, then default collation of database 
Run Code Online (Sandbox Code Playgroud)

或者,@Charlieface(在对此答案的评论中)很友善地提醒我们,有一种更简单的方法可以通过使用很酷的内置动态管理功能来获取此信息,sys.dm_exec_describe_first_result_set并通过db<>fiddle提供了一个工作示例。我更新了该示例以包含该函数的存储过程版本,sys.sp_describe_first_result_set以及上面显示的示例代码。

  • “整理数据库默认值”可能更明智,以防它发生变化。另外 `sys.dm_exec_describe_first_result_set` 可能是查找结果排序规则的更简单方法 https://dbfiddle.uk/vZ_CWm82 (2认同)
  • @Charlieface 感谢您的反馈。回复:`DATABASE_DEFAULT`,我意识到这是一个相当流行的选项,但我相信它的使用频率比应有的要高。对于需要对环境敏感的操作来说,它确实非常有用,特别是当环境可能发生变化或者代码部署到无法保证使用相同排序规则的各个位置时。然而,无论环境如何,从应用程序的角度来看,许多操作都需要保持一致的行为。回复:“dm_exec_describe_first_result_set”,我忘记了这一点,所以我更新了我的答案。 (2认同)