web-dev-qa-db-ja.com

後続スペースを含む値と一致するSQL WHERE句

SQL Server 2008には、Zoneというテーブルがあり、列ZoneReference varchar(50) not nullを主キーとして使用しています。

次のクエリを実行すると:

_select '"' + ZoneReference + '"' as QuotedZoneReference
from Zone
where ZoneReference = 'WF11XU'
_

次の結果が得られます。

_"WF11XU "
_

末尾のスペースに注意してください。

これはどのようにして可能ですか?末尾のスペースが実際にその行にある場合は、結果としてzeroが返されることが予想されるので、SQL Server Management Studioが奇妙に表示しているのは何か他のものであると想定しています。

C#コードでzoneReference.Trim()を呼び出すとそれが削除され、ある種の空白文字であることが示唆されます。

誰か助けてもらえますか?

44
Neil Barnwell

これは予想される結果です。SQLServerでは=演算子は、比較時に末尾のスペースを無視します。

SQL Serverは、文字列をスペースと比較する方法について、ANSI/ISO SQL-92仕様(セクション8.2、、一般規則#3)に従います。 ANSI規格では、比較で使用される文字列の長さを比較して比較する前に、それらの文字列にパディングが必要です。パディングは、WHEREおよびHAVING句の述語のセマンティクスと他のTransact-SQL文字列比較に直接影響します。たとえば、Transact-SQLは、文字列 'abc'と 'abc'をほとんどの比較演算で同等と見なします。

このルールの唯一の例外は、LIKE述部です。 LIKE述語式の右側に後続スペースのある値が含まれている場合、SQL Serverは比較が行われる前に2つの値を同じ長さに埋め込みません。 LIKE述語の目的は、定義により、単純な文字列の等価性テストではなくパターン検索を容易にすることであるため、これは前述のANSI SQL-92仕様のセクションに違反しません。

ソース

67
Mark Byers

末尾のスペースが常に無視されるわけではありません。今日、この問題が発生しました。テーブルにNCHAR列があり、VARCHARデータに結合されていました。テーブル内のデータはフィールドほど幅が広くなかったため、SQL Serverによって末尾のスペースが自動的に追加されました。

VarcharパラメータをとるITVF(インラインテーブル値関数)がありました。パラメータは、NCHARフィールドを持つテーブルへのJOINで使用されました。

関数に渡されたデータに末尾のスペースはなかったが、テーブル内のデータにはあったため、結合は失敗しました。なんで?

DATA TYPE PRECEDENCEでつまずきました。 ( http://technet.Microsoft.com/en-us/library/ms190309.aspx を参照)

異なるタイプの文字列を比較する場合、比較の前に、優先順位の低いタイプが優先順位の高いタイプに変換されます。したがって、VARCHARパラメータはNCHARに変換されました。 NCHARが比較され、明らかにスペースが有意でした。

これをどのように修正しましたか?関数の定義を変更して、NCHARよりも優先度の高いNVARCHARパラメータを使用しました。 NCHARはSQL Serverによって自動的にNVARCHARに変更され、末尾のスペースは無視されました。

RTRIMを実行しなかったのはなぜですか?テストの結果、RTRIMがパフォーマンスを低下させ、SQL Serverが使用するはずだったJOIN最適化を妨げていることが判明しました。

テーブルのデータ型を変更してみませんか?テーブルはすでに顧客のサイトにインストールされており、メンテナンススクリプトを実行したり(DBAに支払う時間と費用)、マシンへのアクセスを提供したりしません(理解できる)。

6
Paul Chernoch

ええ、マークは正しいです。次のSQLを実行します。

create table #temp (name varchar(15))
insert into #temp values ('james ')
select '"' + name + '"' from #temp where name ='james'
select '"' + name + '"' from #temp where name like 'james'
drop table #temp

しかし、「like」ステートメントに関するアサーションは、上記の例では機能しないようです。出力:

(1 row(s) affected)

-----------------
"james "

(1 row(s) affected)


-----------------
"james "

(1 row(s) affected)

編集:機能させるには、最後に次のように記述します。

and name <> rtrim(ltrim(name))

醜いけど。

EDIT2:上記のコメントを考えると、次のように機能します。

select '"' + name + '"' from #temp where 'james' like name
3
James Wiseman