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()
を呼び出すとそれが削除され、ある種の空白文字であることが示唆されます。
誰か助けてもらえますか?
これは予想される結果です。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仕様のセクションに違反しません。
末尾のスペースが常に無視されるわけではありません。今日、この問題が発生しました。テーブルに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に支払う時間と費用)、マシンへのアクセスを提供したりしません(理解できる)。
ええ、マークは正しいです。次の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