在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或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 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’.
最佳答案 在进行比较测试之前,将字段转换为相同的类型:
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