SQL Server 2008 の空の文字列と空間
-
22-09-2019 - |
質問
今朝、ちょっと奇妙なことに遭遇したので、解説のために投稿しようと思いました。
次の SQL クエリが SQL 2008 に対して実行されたときに「等しい」と出力される理由を誰かが説明できますか。db 互換性レベルは 100 に設定されています。
if '' = ' '
print 'equal'
else
print 'not equal'
そして、これは 0 を返します:
select (LEN(' '))
スペースを自動トリミングしているようです。以前のバージョンの SQL Server でこれが当てはまるかどうかはわかりません。また、それをテストする余裕さえありません。
運用クエリが間違った結果を返したため、この問題に遭遇しました。この動作に関する文書はどこにも見つかりません。
これについて何か情報を持っている人はいますか?
解決
varchar
s と等価性は 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
他のヒント
=演算子は、T-SQLは「式のコンテキストの照合に従って「単語/フレーズ」であるように「等しい」ものではなく、レンは「単語/フレーズの文字の数」です。後続のブランクを単語/フレーズの一部として扱う照合はありません(ただし、先行する文字列の一部として先頭のブランクを扱います)。
「this」と「this 」を区別する必要がある場合は、「this」と「this 」は同じ単語であるため、「同じ単語またはフレーズである」演算子を使用しないでください。
= の動作に貢献しているのは、文字列等価演算子は引数の内容と式の照合コンテキストに依存する必要があるが、引数が両方とも文字列型である場合、引数の型には依存すべきではないという考え方です。 。
「これらは同じ単語である」という自然言語の概念は、通常、= などの数学演算子で捉えることができるほど正確ではありません。また、自然言語には文字列型の概念がありません。コンテキスト (つまり、照合) は重要であり (自然言語に存在し)、ストーリーの一部であり、追加のプロパティ (風変わりに見えるものもあります) は、不自然な世界で明確に定義されるように = の定義の一部です。データ。
型の問題では、単語が異なる文字列型で保存されたときに変更されることは望ましくありません。たとえば、VARCHAR(10)、CHAR(10)、CHAR(3) 型はすべて、単語「cat」と ? の表現を保持できます。= 'cat' では、これらの型のいずれかの値が 'cat' という単語を保持するかどうかを判断できます (大文字小文字とアクセントの問題は照合順序によって決まります)。
JohnFx のコメントへの返答:
見る char および varchar データの使用 オンラインブックスで。そのページから引用し、私の言葉を強調します。
char および varchar の各データ値には照合順序があります。照合は、各文字を表すために使用されるビットパターンなどの属性を定義します。 比較ルール, 、大文字と小文字またはアクセント記号の区別。
見つけやすくなるのは同意ですが、文書化されています。
また、注目に値するのは、= が現実世界のデータと比較のコンテキストに関係する SQL のセマンティクス (コンピューターに保存されているビットに関するものではなく) が、長い間 SQL の一部であったことです。RDBMS と SQL の前提は実世界のデータを忠実に表現することであるため、同様のアイデア (CultureInfo など) が Algol のような言語の領域に入る何年も前から照合順序がサポートされていました。これらの言語の前提は (少なくともごく最近まで) エンジニアリングにおける問題解決であり、ビジネス データの管理ではありませんでした。(最近、検索などの非エンジニアリング アプリケーションでの同様の言語の使用がある程度浸透してきていますが、Java や C# などは、その非ビジネス的なルーツに依然として苦戦しています。)
私の意見では、SQLが "ほとんどのプログラミング言語 "と違うからといって批判するのはフェアではない。 SQLは、エンジニアリングとはまったく異なるビジネス・データ・モデリングのフレームワークをサポートするために設計された。
そうですね、SQL が最初に指定されたとき、一部の言語には組み込みの文字列型がありませんでした。そして、一部の言語では依然として、文字列間の等号演算子は文字データをまったく比較せず、参照を比較します。あと 10 年か 20 年後には、== は文化に依存するという考えが標準になったとしても、私は驚かないでしょう。
私はこのブログを見つけました行動を説明し、理由を説明する記事でます。
の MSKB316626 ののでの SQL標準では、その文字列が必要です 比較、効果的に、パッドのインクルード 短い空白文字を含む文字列。を 驚くべき結果にこのリード そのN「」= N」 '(空の文字列 一個の以上のスペースの文字列に等しいです 文字)より一般的に任意の 彼らならば文字列が別の文字列に等しいです 唯一の末尾のスペースによって異なります。この いくつかの状況で問題になることがあります。
その他の情報も利用可能
少し前に同様の質問があり、同様の問題を調べました ここ
LEN(' ') の代わりに DATALENGTH(' ') を使用すると、正しい値が得られます。
解決策は、私の回答で説明したように LIKE 句を使用するか、WHERE 句に 2 番目の条件を含めて DATALENGTH もチェックすることでした。
その質問とそこにあるリンクを読んでください。
リテラル空間に値を比較するには、あなたはまた、LIKE文の代替として、この技術を使用することができます:
IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'
時には1はヌルを使用してのアイデアが優れているにもかかわらず、他の文字の有無にかかわらず、データ内のスペースに対処する必要がある - が、常に使用できません。 私が説明したような状況に遭遇し、それをこのように解決しました。
...ここで、( '>' + @space + '<')<>( '>' + @ SPACE2 + '<')
もちろん、あなたがデータのFPR大量にしないだろうが、それはいくつかの100行のために迅速かつ簡単に動作します...
ハーバート
SQLサーバー上でフィールドchar/varcharを使用して選択時にレコードを区別する方法:例:
declare @mayvar as varchar(10)
set @mayvar = 'data '
select mykey, myfield from mytable where myfield = @mayvar
期待される
mykey (int) | myfield (varchar10)
1 | 'データ'
得られた
マイキー|マイフィールド
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 にインデックスがある場合は、それぞれの場合にそれが使用されます。
お役に立てば幸いです。
もう一つの方法は、スペースが値を持っている状態に戻ってそれを置くことです。 例えば:_
のように知られている文字とスペースを置き換えますif REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
print 'equal'
else
print 'not equal'
戻り値:等しくない
理想的な、そしておそらく遅いが、すぐに必要なときに別の簡単な方法前方にあるわけではありません。