2016-08-22 15 views
4

SQL Serverではnvarcharの値はUnicodeコードポイントの文字列を表します。デフォルトではUTF-16を使用していますが、0xFFFFを超える値はサロゲートペアとして表されます。nvarcharの比較で0x8FFFの意義は何ですか?

nvarchar UDFパラメータに、特殊文字を含むデフォルトの文字列値を設定したかったのですが、 T-SQLでは文字列リテラルで16進エスケープシーケンスを使用できないため、CHAR()またはNCHAR()関数を使用してコードポイント値で文字を指定する必要がありますが、パラメータの既定値にはリテラルを使用する必要があります。NCHAR()は使用できません。

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 Serverはまた、そう、varbinaryからnvarcharへの暗黙的な変換を実行することを思い出しましたしたがって、私はOPTION(RECOMPILE)または今は信用されていない(@foo IS NULL OR Table.Foo = @foo)パターンを必要とせずに、検索機能に静的SQLを使用させます。

だから私はこれに私の機能を変更:

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は失敗しました。

私が知っている限り、ユニコードでは0x8FFF - 0x9000の境界について特別なことはありません。 https://en.wikipedia.org/wiki/Plane_(Unicode)#/media/File:Roadmap_to_Unicode_BMP.svgとUTF-16サロゲートが0xD8000xDC00で開始 - 遠く0x900から:0xFFFF0x900は、単にCJKエリア内の別のブロックであること - ウィキペディアは、基本多言語面が0x0000を占めて報告します。 0x8000境界が、他の境界あまりにも - だから、それだけで0x7FFFないようです

 
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 

 
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) 

そして私の結果:

は、ここに私のテストケースです。それがリトルエンディアンの代わりに、ビッグエンディアンとしてバイナリリテラルを解釈しているためかもしれないが、彼らはN'H'より大きくしているので、その後**FFで終わるリテラルのすべてがtrueを返す場合

は、私は疑問に思いました。

+0

あなたは無視している*照合* SQLで*ソート順*を定義します。 *バイナリ*照合を強制すると、範囲比較の大部分は –

+0

"*私が構築していたシステムでスローされた(実用的な)Unicodeテキストを収容するのに十分な0xFFFFと推測されます*東アジアのテキスト、絵文字、記号などを扱う必要はありません。UTF-16でサロゲートペアを必要とします。 –

+0

パラメータをNULLにデフォルト設定し、その機能を内部的にチェックするのはどうでしょうか?次に、パラメータ宣言に実際の文字列リテラルは必要なく、宣言を変更することなく、時間の経過とともにデフォルトを変更することができます。 –

答えて

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 
+0

これは奇妙です - コードでは、毎回 'yup'を返します。これは' (暗黙の) 'varbinary'から' nvarchar'への変換の代わりに 'nvarchar'を' varbinary'に変換します。私はこれが照合と関係していると考えています。 'varbinary'と' varbinary'の比較はできません。なぜなら、列のインデックス(カラムの 'nvarchar'型で構築されている)を使用しないからです。 – Dai

関連する問題