【发布时间】:2016-12-28 14:59:57
【问题描述】:
在 SQL Server 中,nvarchar 值表示 Unicode 代码点字符串 - 我理解,默认情况下,使用 UTF-16,0xFFFF 以外的值表示为代理对。
我想为包含特殊字符的nvarchar UDF 参数设置一个默认字符串值。 T-SQL 不允许您在字符串文字中使用十六进制转义序列,您必须使用 CHAR() 或 NCHAR() 函数通过其代码点值指定字符,但是您必须使用文字作为参数默认值:您不能使用NCHAR()。不过我记得 SQL Server 也会执行从varbinary 到nvarchar 的隐式转换,所以:
CREATE FUNCTION DoSomething(
@foo nvarchar(50) = '\x0008', -- not supported by T-SQL syntax
@bar nvarchar(50) = NCHAR(8), -- forbidden: defaults must be a literal
@baz nvarchar(50) = 0x008 -- success!
)
我想更改参数以表示比较范围,并且我希望默认值表示最广泛的值范围,从而让我使用静态 SQL 进行搜索功能,而不需要 OPTION(RECOMPILE) 或现在-名誉扫地的(@foo IS NULL OR Table.Foo = @foo) 模式。
所以我把我的功能改成了这样:
CREATE FUNCTION DoSomething(
@fooMin nvarchar(50) = 0x0000,
@fooMax nvarchar(50) = 0xFFFF
)
/* SELECT goes here */
WHERE
Foo BETWEEN @fooMin AND @fooMax
我推断0xFFFF 足够高,可以容纳在我正在构建的系统中抛出的任何(实用)unicode 文本。
然而,令我惊讶的是,BETWEEN 运算符总是返回 false。我想知道上限操作数是否有问题,所以我将其更改为0x7FFF,它运行良好。
接下来我尝试了0x8FFF,这也奏效了。
但是0x9FFF 然后0x9000 失败了。
据我所知,Unicode 中的0x8FFF - 0x9000 边界没有什么特别之处。维基百科报告基本多语言平面占据0x0000 - 0xFFFF,0x900 只是 CJK 区域中的另一个块:https://en.wikipedia.org/wiki/Plane_(Unicode)#/media/File:Roadmap_to_Unicode_BMP.svg 和 UTF-16 代理从 0xD800 和 0xDC00 开始 - 远离0x900.
这是我的测试用例:
SELECT N'HELLO', 0xFF, ( CASE WHEN HELLO' BETWEEN 0x0000 AND 0xFF THEN 'yup' ELSE 'no' END ) 联合所有 SELECT N'HELLO', 0x0FFF, (当 N'HELLO' BETWEEN 0x0000 和 0x0FFF THEN 'yup' ELSE 'no' END 时的情况) 联合所有 SELECT N'HELLO', 0x1000, ( CASE WHEN HELLO' BETWEEN 0x0000 AND 0x1000 THEN 'yup' ELSE 'no' END ) 联合所有 SELECT N'HELLO', 0x6000, ( CASE WHEN HELLO' BETWEEN 0x0000 AND 0x6000 THEN 'yup' ELSE 'no' END ) 联合所有 SELECT N'HELLO', 0x6FFF, (当 N'HELLO' BETWEEN 0x0000 和 0x6FFF THEN 'yup' ELSE 'no' END 时的情况) 联合所有 SELECT N'HELLO', 0x7000, ( CASE WHEN HELLO' BETWEEN 0x0000 AND 0x7000 THEN 'yup' ELSE 'no' END ) 联合所有 SELECT N'HELLO', 0x7FFF, (当 N'HELLO' BETWEEN 0x0000 和 0x7FFF THEN 'yup' ELSE 'no' END 时的情况) 联合所有 SELECT N'HELLO', 0x8000, ( CASE WHEN HELLO' BETWEEN 0x0000 AND 0x8000 THEN 'yup' ELSE 'no' END ) 联合所有 SELECT N'HELLO', 0x8FFF, (当 N'HELLO' BETWEEN 0x0000 和 0x8FFF THEN 'yup' ELSE 'no' END 时的情况) 联合所有 SELECT N'HELLO', 0x9000, ( CASE WHEN HELLO' BETWEEN 0x0000 AND 0x9000 THEN 'yup' ELSE 'no' END ) 联合所有 SELECT N'HELLO', 0x9FFF, (当 N'HELLO' BETWEEN 0x0000 和 0x9FFF THEN 'yup' ELSE 'no' END 时的情况) 联合所有 SELECT N'HELLO', 0xFFFF, (当 N'HELLO' BETWEEN 0x0000 和 0xFFFF THEN 'yup' ELSE 'no' END 时的情况)我的结果:
你好 0xFF 是的 你好 0x0FFF 没有 你好 0x1000 没有 你好 0x6000 没有 你好 0x6FFF 是的 你好 0x7000 是的 你好 0x7FFF 是的 你好 0x8000 没有 你好 0x8FFF 是的 你好 0x9000 没有 你好 0x9FFF 没有 你好 0xFFFF 没有所以看起来它不仅仅是0x7FFF - 0x8000 边界,还有其他边界。
我想知道是否可能是因为它将二进制文字解释为 little-endian 而不是 big-endian,但是所有以 **FF 结尾的文字都会返回 true,因为它们大于 N'H' .
【问题讨论】:
-
您忽略了在 SQL 中定义 sort ordering 的 collations。如果您强制使用 binary 排序规则,您应该会发现大多数范围比较都有效
-
"我推断 0xFFFF 足够高,可以容纳在我正在构建的系统中抛出的任何(实用)unicode 文本" - 前提是您不必处理东亚文字、表情符号、符号等。在 UTF-16 中需要代理对的东西。
-
简单地将参数默认为 NULL 并让您的函数在内部检查呢?然后,您不需要在参数声明中使用实际的字符串文字,并且可以随着时间的推移更改您的默认值,而无需更改您的声明。
-
@RemyLebeau 会导致 SQL Server 执行计划欠佳:sommarskog.se/dyn-search.html
标签: sql sql-server unicode