在nvarchar比较中0x8FFF的意义是什么?

时间:2016-08-22 07:01:00

标签: sql sql-server unicode

在SQL Server中,0xFFFF值表示Unicode代码点的字符串 - 我默认使用UTF-16,超出nvarchar的值表示为代理对。

我想为包含特殊字符的CHAR() UDF参数设置默认字符串值。 T-SQL不允许您在字符串文字中使用十六进制转义序列,您必须使用NCHAR() OR NCHAR()函数按其代码点值指定字符,但是您必须使用文字作为参数默认值:你不能使用varbinary。但是我记得SQL Server也执行从nvarcharCREATE 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! ) 的隐式转换,所以:

OPTION(RECOMPILE)

我想更改参数以表示比较范围,我希望默认值代表最宽可能的值范围,这样我就可以将静态SQL用于搜索功能而无需(@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

我推断BETWEEN足够高,可以容纳在我正在构建的系统中抛出的任何(实际)unicode文本。

但令我惊讶的是,false运算符始终返回0x7FFF。我想知道是否某些事情可能与上限操作数有关,所以我将其更改为0x8FFF并且它工作正常。

我接下来尝试了0x9FFF,这也很有用。

0x9000然后0x8FFF失败。

据我所知,Unicode中的0x9000 - 0x0000边界没什么特别之处。维基百科报告基本多语言平面占据0xFFFF - 0x9000xD800只是CJK区域中的另一个区块:https://en.wikipedia.org/wiki/Plane_(Unicode)#/media/File:Roadmap_to_Unicode_BMP.svg,UTF-16代理从{{1}开始}}和0xDC00 - 远离0x900

这是我的测试用例:

SELECT N'HELLO', 0xFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0xFF THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x0FFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x0FFF THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x1000, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x1000 THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x6000, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x6000 THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x6FFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x6FFF THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x7000, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x7000 THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x7FFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x7FFF THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x8000, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x8000 THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x8FFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x8FFF THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x9000, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x9000 THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0x9FFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0x9FFF THEN 'yup' ELSE 'no' END )
UNION ALL
SELECT N'HELLO', 0xFFFF, ( CASE WHEN N'HELLO' BETWEEN 0x0000 AND 0xFFFF THEN 'yup' ELSE 'no' END )

我的结果:

HELLO   0xFF    yup
HELLO   0x0FFF  no
HELLO   0x1000  no
HELLO   0x6000  no
HELLO   0x6FFF  yup
HELLO   0x7000  yup
HELLO   0x7FFF  yup
HELLO   0x8000  no
HELLO   0x8FFF  yup
HELLO   0x9000  no
HELLO   0x9FFF  no
HELLO   0xFFFF  no

所以它似乎不只是0x7FFF - 0x8000边界,而是其他边界。

我想知道是否可能是因为它将二进制文字解释为little-endian而不是big-endian,但是以**FF结尾的所有文字都会返回true因为它们更大比N'H'

1 个答案:

答案 0 :(得分:1)

在进行比较测试之前,将字段转换为相同的类型:

select CASE WHEN convert(varbinary(82), N'HELLO') BETWEEN 0x0000 AND 0xffff THEN 'yup' ELSE 'no' END
select CASE WHEN convert(varbinary(82), N'HELLO') BETWEEN 0x4800 AND 0xffff THEN 'yup' ELSE 'no' END
select CASE WHEN convert(varbinary(82), N'HELLO') BETWEEN 0x4800 AND 0x4801 THEN 'yup' ELSE 'no' END

declare @x1 nvarchar(2) = 0x4800, @x2 nvarchar(2) = 0xFFFF;
declare @l1 nvarchar(2) = reverse(convert(varbinary(2), @x1));
declare @l2 nvarchar(2) = reverse(convert(varbinary(2), @x2));
select CASE WHEN N'HELLO' BETWEEN @l1 AND @l2 THEN 'yup' ELSE 'no' END