SQL Server2008の空の文字列とスペース


82

今朝少し変なことに出くわし、解説のために提出しようと思いました。

次のSQLクエリがSQL2008に対して実行されたときに「等しい」と出力される理由を誰かが説明できますか。db互換性レベルは100に設定されています。

if '' = ' '
    print 'equal'
else
    print 'not equal'

そしてこれは0を返します:

select (LEN(' '))

スペースを自動トリミングしているようです。これが以前のバージョンのSQLServerに当てはまるかどうかはわかりません。また、テストすることすらできなくなりました。

本番クエリが誤った結果を返していたため、これに遭遇しました。この動作が文書化されている場所はどこにも見つかりません。

誰かがこれに関する情報を持っていますか?


2
SQL 2005:select len( '')は0を返します
Mayo

1
これは、SQL Server 2000で同じことを
ピエール・アランVigeant

1
これは魅力的な質問です。一致するかどうかに関係なく、いずれかの文字列にいくつのスペースを入れても、等しい値を返すようです。さらに実験した後、比較の前に、等式演算子の両側でRTRIMを効果的に実行していることに気付きました。LEN関数について回答を得たようですが、質問の平等部分については、「TSQではvarcharsとequalityは厄介です」よりも徹底的な回答に興味があります。
JohnFx 2009

Oracleもこれを行っていると思います。
quillbreaker 2009

一般に、空の文字列を格納することは悪い考えであり、これが理由の1つであることがわかります。私はNullの使用を好み、人々がnull情報を空の文字列や通常の範囲外のデータのような値にしようとすると、多くの問題を見つけます。
HLGEM 2011

回答:


87

varcharsと平等はTSQLでは厄介です。LEN機能は言います:

末尾の空白を除いて、指定された文字列式のバイト数ではなく文字数を返します。

問題のデータのDATALENGTH真のbyte数を取得するには、を使用する必要があります。Unicodeデータがある場合、この状況で取得する値はテキストの長さと同じではないことに注意してください。

print(DATALENGTH(' ')) --1
print(LEN(' '))        --0

式の同等性に関しては、2つの文字列が次のように同等であるかどうかが比較されます。

  • 短い文字列を取得
  • 長さが長いストリングの長さと等しくなるまでブランクでパッドします
  • 2つを比較する

予期しない結果を引き起こしているのは中間のステップです。そのステップの後、空白と空白を効果的に比較しているため、それらは等しいように見えます。

LIKE=一致させようとしたパターンに対して空白のパディングを実行しないため、「空白」の状況よりも動作が優れています。

if '' = ' '
print 'eq'
else
print 'ne'

eqしばらくの間与えるでしょう:

if '' LIKE ' '
print 'eq'
else
print 'ne'

あげる ne

LIKEただし、注意が必要です。対称ではありません。末尾の空白はパターン(RHS)で重要として扱われますが、一致式(LHS)では扱われません。以下はここから取られています

declare @Space nvarchar(10)
declare @Space2 nvarchar(10)

set @Space = ''
set @Space2 = ' '

if @Space like @Space2
print '@Space Like @Space2'
else
print '@Space Not Like @Space2'

if @Space2 like @Space
print '@Space2 Like @Space'
else
print '@Space2 Not Like @Space'

@Space Not Like @Space2
@Space2 Like @Space

1
いい答えだ。LENのドキュメントではそのことに気づいていませんでした。ただし、LENに限定されません。RIGHT関数とLEFT関数は同様の動作を示しますが、文書化されていません。問題の原因はスペースのあるリテラルのようです。これもequalを返すことに気づきました:if '' = SPACE(1)print'equal 'else print'not equal'本当の長さを取得することにあまり興味がないので、スペースを探していたときになぜ混乱したのですか?列、空の文字列であったすべての列が返されました。
jhale 2009

また、LIKEステートメントに関する優れた情報。物語の教訓は、スペースと空の文字列を比較する必要がある位置に身を置かないようにすることだと思います。
jhale 2009

2
この問題は、スペースを空の文字列と比較するよりも大きな問題です。スペースの数が異なる2つの文字列を比較すると、同じ動作を示します。
JohnFx 2009

3
@butterchicken:投稿が遅くなってすみません、この質問を見たばかりですが、これ(最後の質問)sql-server-2008 r2を実行すると、が表示され@Space Not Like @Space2 @Space2 Not Like @Space ます。理由は何ですか?
Razort4x 2013

1
SQL Server2012およびSQLServer 2014で確認された結果は@Space Not Like @Space2 @Space2 Not Like @Space
、次のとおり

19

=演算子はT-SQLであり、「式のコンテキストの照合によると、同じ単語/フレーズである」ほど「等しい」ものではなく、LENは「単語/フレーズの文字数」です。照合では、末尾の空白をその前の単語/句の一部として扱いません(ただし、先頭の空白を前の文字列の一部として扱います)。

'this'と 'this'を区別する必要がある場合は、 'this'と 'this'が同じ単語であるため、 "are the same word orphrase"演算子を使用しないでください。

=の動作に貢献しているのは、文字列等価演算子は引数の内容と式の照合コンテキストに依存する必要があるという考えですが、両方が文字列タイプである場合は、引数のタイプに依存しないでください。 。

「これらは同じ単語です」という自然言語の概念は、通常、=のような数学演算子で取得できるほど正確ではなく、自然言語には文字列タイプの概念はありません。コンテキスト(つまり、照合)は重要であり(そして自然言語で存在し)、ストーリーの一部であり、追加のプロパティ(風変わりに見えるものもあります)は、の不自然な世界で明確に定義するために=の定義の一部です。データ。

タイプの問題では、単語が異なる文字列タイプに格納されているときに単語を変更したくないでしょう。たとえば、タイプVARCHAR(10)、CHAR(10)、およびCHAR(3)はすべて、単語「cat」および?の表現を保持できます。= 'cat'は、これらのタイプのいずれかの値が単語 'cat'を保持するかどうかを決定できるようにする必要があります(大文字と小文字およびアクセントの問題は照合によって決定されます)。

JohnFxのコメントへの応答:

BooksOnlineでのcharおよびvarcharデータの使用を参照してください。そのページから引用して、私の強調:

各charおよびvarcharデータ値には照合順序があります。照合順序は、各文字を表すために使用されるビットパターンなどの属性を定義します。 比較規則、大文字と小文字またはアクセントに対する感度。

見つけやすいかもしれないことに同意しますが、文書化されています。

また、注目に値するのは、SQLのセマンティクス(=は実際のデータと関係があり、比較のコンテキスト(コンピューターに格納されているビットに関するものとは対照的)は長い間SQLの一部であったことです。RDBMSとSQLの前提は、実世界のデータを忠実に表現することです。したがって、同様のアイデア(CultureInfoなど)がAlgolのような言語の領域に入る何年も前に、照合をサポートします。これらの言語の前提は(少なくともごく最近まで)、ビジネスデータの管理ではなく、エンジニアリングにおける問題解決でした。(最近、検索などの非エンジニアリングアプリケーションで同様の言語を使用することで、ある程度の浸透が見られますが、Java、C#などは依然として非ビジネスのルーツに苦しんでいます。)

私の意見では、SQLが「ほとんどのプログラミング言語」とは異なると批判するのは公平ではありません。SQLは、エンジニアリングとは大きく異なるビジネスデータモデリングのフレームワークをサポートするように設計されているため、言語が異なります(そしてその目標に適しています)。

ちなみに、SQLが最初に指定されたとき、一部の言語には組み込みの文字列型がありませんでした。また、一部の言語では、文字列間のequals演算子は文字データをまったく比較しませんが、参照を比較します。さらに10年か2年後に、==が文化に依存するという考えが標準になったとしても、私は驚かないでしょう。


BOLは、=演算子を次のように記述します。「2つの式の等価性を比較します(比較演算子)。」動作が正しいかどうかにかかわらず、ほとんどのプログラミング言語でのこの演算子の使用法に関して、非常に混乱し、非標準であることを認める必要があります。MSは、少なくともこの動作に関する警告をドキュメントに追加する必要があります。
JohnFx 2009

@JohnFx:私の回答でコメントするには長すぎる応答を参照してください。
Steve Kass

9

動作とその理由を説明するこのブログ記事を見つけました。

SQL標準では、文字列の比較では、効果的に短い文字列にスペース文字を埋め込む必要があります。 これは、N '' = N ''(空の文字列は1つ以上のスペース文字の文字列に等しい)という驚くべき結果につながります。より一般的には、末尾のスペースだけが異なる場合、任意の文字列は別の文字列に等しくなります。これは、状況によっては問題になる可能性があります。

詳細については、MSKB316626もご覧ください。


ありがとう。標準になっているのには驚きました。私よりずっと賢い人がこれには正当な理由があると確信しています。
jhale 2009

@ジョン:コメントに≠(等しくない)と書くつもりでしたか?
Steve Kass

元の見積もりにエラーがあり、直接コピーしました。元の著者の意味を反映するように見積もりを更新しました。
JohnFx 2009

5

しばらく前に同様の質問があり、ここで同様の問題を調べました

の代わりにLEN(' ')DATALENGTH(' ')-を使用して正しい値を取得します。

解決策はLIKE、そこにある私の答えで説明されている条項を使用するか、WHERE条項に2番目の条件を含めて確認するDATALENGTHことでした。

その質問とそこにあるリンクを読んでください。


3

値をリテラルスペースと比較するには、LIKEステートメントの代わりにこの手法を使用することもできます。

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'

0

SQLサーバーのフィールドchar / varcharを使用してselectのレコードを区別する方法:例:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

期待される

mykey(int)| myfield(varchar10)

1 | 'データ'

得られた

mykey | マイフィールド

1 | 'データ' 2 | 'データ'

select mykey, myfield from mytable where myfield = 'data'(最後の空白なしで)書いても 、同じ結果が得られます。

どのように解決しましたか?このモードの場合:

select mykey, myfield
from mytable
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)

myfieldにインデックスがある場合は、それぞれの場合に使用されます。

お役に立てば幸いです。


0

もう1つの方法は、スペースに価値がある状態に戻すことです。例:スペースを_のような既知の文字に置き換えます

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

戻り値:等しくない

理想的ではなく、おそらく遅いですが、必要に応じてすばやく前進するもう1つの方法です。


0

Nullを使用するというアイデアの方が優れている場合でも、他の文字の有無にかかわらず、データ内のスペースを処理する必要がある場合がありますが、常に使用できるとは限りません。私は説明された状況に遭遇し、次のように解決しました:

... where ('>' + @space + '<') <> ('>' + @space2 + '<')

もちろん、大量のデータに対してはそうしませんが、数百行の場合はすばやく簡単に機能します...


1
問題は SQLサーバーがそのように動作する理由であり、そのような動作を一般的に処理する方法ではありませんでした。jhaleはおそらく、プログラムコードを変更せず、サーバー構成のみを変更したいと考えています。
Lutz Prechelt 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.