2017-02-22 10 views
2

varcharとnvarcharのデータを使用した「順序付け」の結果の間に予期しない違いが発生しました。どちらの場合も、問題のデータは古いASCII文字セットからのものです。 nnn vs -nnnで始まるデータの順序付けに差が生じます.nは数字です。varcharとnvarcharのSQL Serverの予期しない「順序付け」の差異

以下は、問題を再現するSQL Serverスクリプトです。私のテストサーバーはSQL 2016ですが、私は2008年と2012年にも問題を再現しました。私は何の効果もない別の照合を試みました(Latin1_General_binを除く、以下を参照)。このスクリプトでは、アプリケーションに似た2つのサンプルテーブルを作成します.1つはvarcharともう1つのnvarcharを使用し、7つのデータ行を追加します。

CREATE TABLE [dbo].[_ValidationLists](
    [_FldNum] [int] NOT NULL, 
    [_ValidationEntry] [varchar](250) NOT NULL, 
CONSTRAINT [PK__ValidationLists] PRIMARY KEY CLUSTERED 
(
    [_ValidationEntry] ASC, 
    [_FldNum] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

CREATE TABLE [dbo].[_ValidationListsN](
    [_FldNum] [int] NOT NULL, 
    [_ValidationEntry] [nvarchar](250) NOT NULL, 
CONSTRAINT [PK__ValidationListsN] PRIMARY KEY CLUSTERED 
(
    [_ValidationEntry] ASC, 
    [_FldNum] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

INSERT INTO [_ValidationLists] (_fldnum, _ValidationEntry) VALUES (1,'-1'), (1,'-10'), (1,'-100'), (1,'0'), (1,'1'), (1,'10'), (1,'100') 
INSERT INTO [_ValidationListsN] (_fldnum, _ValidationEntry) VALUES (1,N'-1'), (1,N'-10'), (1,N'-100'), (1,N'0'), (1,N'1'), (1,N'10'), (1,N'100') 


select * from [_ValidationLists] 
order by [_ValidationEntry] asc 
select * from [_ValidationListsN] 
order by [_ValidationEntry] asc 

select文の結果は次のとおりです。 varcharの最初の結果は、私が期待しているものです(辞書順ソート)。私が説明することができない2番目の結果。 1つ目は、顧客ベースも期待していることであり、私たちはこの結果によって驚かされました。 (顧客データは珍しい - 通常、この表はアルファデータに使用され、アルファデータ注文はvarcharとnvarcharの両方で同じです)。

結果は、N '...'を使用して_ValidationListsN行を初期化するのと同じです。元のデータには、「-100:合格」などの長い記入項目がありました。私は、問題を示すデータを少なくとも編集しました。

すべてのエントリが同じ長さになるように、ブランクを使った右パディングは効果がありません。

COLLATEの使用Latin1_General_binは辞書順ソートを再現しますが、(1つの理由のみで)大文字小文字を区別しないソートを使用するため、受け入れられません。

この問題を報告したカスタマーにはASCIIデータしかないので、varcharを使用してこのテーブルを再作成して修正することができます。私は結果が私には間違っているように見えるので、nvarcharがこのように動作する理由を知りたいと思っています。また、私たちが期待する注文行動を得る方法がある場合(最初のケース)。少なくとも、私は ' - '(ASCII 0X2d、ダッシュまたはマイナス記号)で始まるすべてのエントリが一緒に注文しないのはなぜか分かりません。

_FldNum _ValidationEntry

1   -1 
1   -10 
1   -100 
1   0 
1   1 
1   10 
1   100 

(7行(複数可)の影響を受ける)

_FldNum _ValidationEntry

1   0 
1   1 
1   -1 
1   10 
1   -10 
1   100 
1   -100 

(影響7行(複数可))

+0

データが数値の場合は、数値データ型を使用してください。 – HLGEM

+0

データは一般的に英数字です。実際の顧客データ項目では、 '-123:Pass'のようになります。私は効果を示す最小限のデータを煮詰めました。 – RFaneuf

答えて

0

SQL Serverの解決さORDER BY、照合だけでなく行の順序も決定します、すでに発見したように、データ型から取得することもできます。

varcharとncharの両方が最後のバイナリにあります。問題は、varcharでは " - "記号が数値/文字の後に来ることです(2進表記の場合).nvarcharの場合は逆です。インターネットでASCI/UNICODEテーブルを確認してください。

このため、ORDER BYが最後にあり、バイナリと比較すると、nvarcharでは負の値が最初に表示されます。

実際にデータがどのように格納されているかに関心がある場合は、「Microsoft SQL Server Internals」という書籍が面白いかもしれません。この問題について議論するセクションがあります。

EDIT:

このスニペットを参照して、データが実際にDB内に格納されているか、参照するには、次のクエリの

SELECT 
    [_ValidationEntry], 
    CONVERT(binary(6), [_ValidationEntry]) AS [BinaryRepresentation] 
FROM [_ValidationLists] 
--ORDER BY [_ValidationEntry] COLLATE Latin1_General_bin 

SELECT 
    [_ValidationEntry], 
    CONVERT(binary(6), [_ValidationEntry]) AS [BinaryRepresentation] 
FROM [_ValidationListsN] 
--ORDER BY [_ValidationEntry] COLLATE Latin1_General_bin 

結果はそのようなものです:

0  0x300000000000 
1  0x310000000000 
-1  0x2D3100000000 
10  0x313000000000 
-10 0x2D3130000000 
100 0x313030000000 
-100 0x2D3130300000 

BINを使用してcollat​​ion、columnはバイナリ表現で順序付けされているため、2Dマイナス記号が最初に来ます。

+0

私はあなたの反応を理解するかどうか分からない。データ型の並べ替えで '-'が発生した場合は、' --'で始まるすべての項目が一緒になるべきではありませんか? – RFaneuf

+0

はい、そうです。私にとって奇妙に思えるのは、 '-'エントリーが他のエントリーとインターリーブされていることです。 nvarcharが '-'を別の方法で扱う場合、例えばASCIIの英数字の後に並び替える場合は、' - 'で始まるすべてのエントリが一緒に並べ替えられると期待します。 – RFaneuf

+0

もう一度チェックしました。_BIN照合、バイナリ照合を使用している場合は、https://msdn.microsoft.com/en-us/library/ms143515(v=sql.105).aspxを参照してください。バイナリ照合では、文字、特にnvarcharsをソートする別の方法。 _BIN2の照合を使用すると役立ちます。 – sqlady

関連する問題