TimberLandChapelのTech Blog

TimberLandChapel が提供する技術Tips,思いつきメモ,雑感ブログです。
Office 互換リボン Release 1.0 をリリースしました。

購読

今期のアンケート

INETAJ

Microsoft

SQL Server

TLC.com

クリエイティブ・コモンズ

情報処理関係官公庁

【SQL Server 2005】Japanese_CI_AS の長音処理がおかしい
この記事は@IT さんの Database Expert 会議室に投稿した記事のフォローアップです。
解決のために
修正が製品版に間に合いますように,より多くの方の検証・投票が必要です。
ご協力ください
http://lab.msdn.microsoft.com/ProductFeedback/viewfeedback.aspx?feedbackid=c540b3c7-fce2-47cc-95e6-fd780172ebea
問題の概要
SQL Server 2005 CTP June において,
Japanese_90_CI_AS,Japanese_CI_AS 照合順序を使用して,
LIKE 演算子に 「ー」(日本語長音)を含む2文字以上の文字列を処理すると
正しく抽出ができなくなる。
問題の再現

再現方法は C# MVP の中さんが簡潔にまとめてくださっています。 http://blogs.wankuma.com/naka/archive/2005/09/29/18540.aspx

ここでは,簡単におさらいを

varchar または,nvarchar を格納するテーブルを作成して,
「ー」(日本語長音)を含む2文字以上の文字列(ここでは「クー」)を条件とするクエリを発行します。

クエリは,実験するためのテーブルを準備して,実験コードを実行した後,後処理を実行します。

再現コード
create database CollateTest
go
 
use CollateTest
go
 
create table JpnTest ( [Data] nvarchar(50) collate Japanese_CI_AS);
 
go
 
insert into JpnTest([Data]) values('クーリング')
insert into JpnTest([Data]) values('カーリング')
insert into JpnTest([Data]) values('ケビン')
 
select * from JpnTest where [Data] like '%クー%'
go
 
drop table JpnTest
go
 
create table JpnTest ([Data] nvarchar(50) collate Japanese_bin);
 
go
 
insert into JpnTest([Data]) values('クーリング')
insert into JpnTest([Data]) values('カーリング')
insert into JpnTest([Data]) values('ケビン')
 
select * from JpnTest where [Data] like '%クー%'
go
 
drop table JpnTest
go

期待される結果は,両方とも「クーリング」が返されることですが,
[Japanese_CI_AS] では期待する結果が返ってきません。

「ー」ではなく「〇」(漢数字の零)が長音文字になっている

結果的に,[Japanese_CI_AS] で,「クーリング」をヒットさせるには,

WHERE [Data] LIKE '%ク〇%'

として,「漢数字の〇」を長音文字としてあててあげなければなりません。
これは日本語の処理としては非常に不適切です。

再現コード
SELECT [DATA]
    FROM [CollateTest].[dbo].[JpnTest]
    WHERE [DATA] LIKE '%ク〇%' COLLATE Japanese_90_CI_AS
 
SELECT [DATA]
    FROM [CollateTest].[dbo].[JpnTest]
    WHERE [DATA] LIKE '%ク〇%' COLLATE Japanese_BIN
GO

検証での考え方

さて,ここからはちょっと趣向を変えて,

「漢数字の零」が長音文字の代わりにヒットしてしまっている

ということを発見するまでに,TLC が何を考えたのか?
について書いてみたいと思います。
このなんでもない探求法を使ってみれば,みなさんでもおかしな動きの検証ができるようになるのではないでしょうか?

まず始めに,

[Japanese_CI_IS] は [SQL Server 2000] でもおかしな動きをしていた

という知識がありました。
特に長音記号に関する部分は,アルファベットなどの文字と組み合わせると不思議な動きをしてくれます。
この時から,「特定の文字」と「特定の文字」を組み合わせる状況によって「不審な動作をする」という経験的な知識が出来上がっていたのです。

この前提に立つと,

今回の動作も「ー」の代わりに何かの文字が代わりにヒットするのではないか?

という疑いが浮かんできます。
このアイデアが浮かびさえすれば,後は総当り的に文字を試してみることで検証ができるのです。

実行したいことは,

「クー」の「ー」の部分をいろいろな文字に置き換えて,実際にクエリを投げてみてみることです

そうすると,
SQL ステートメントの nchar() 関数が使えそうだと思いつきます。
後は,nchar() 関数を使用して総当たり戦をうまく挑むだけですね。

実は,この辺は,それぞれの得意分野に持ち込んでもいいかも知れません。
つまり,ADO.NET を使用してクエリを組み立てるほうが,T-SQL ステートメントを操作するよりも得意な場合は,
通常のプログラミングの世界でループを組めばいいわけです。
ただし,このときには「追加で使用するテクノロジとのつなぎ目の部分で検証の範囲が広がってしまう」ということを念頭においておくことが必須です。
なるべくシンプルに検証できた方がよいわけですね。

さて,それでは実際に使用したコードです。

「クー」の代わりにヒットする文字を探す
DECLARE @count int
DECLARE @limit int
DECLARE @CMD nvarchar(1000)
 
SET @count = 0
SET @limit = @count + 65535
 
WHILE @count < @limit
BEGIN
SET @count = @count + 1
 
SET @CMD = 'SELECT [DATA],'
    + CAST(@count AS nvarchar) +
    ' AS [char] FROM [TestDB].[dbo].[JpnTest] WHERE [DATA] LIKE ''%ク'
    + nchar(@count) + '%'' COLLATE Japanese_CI_AS'
 
EXEC sp_Executesql @CMD
 
END
GO

実行していることは,
クの直後の文字を総当り的に置き換えて,結果を返させているだけです。

結果の検証

このクエリを実行すると,クーリングをヒットさせる文字が3種類あることがわかります。
その3種類とは,

  • 「_」(アンダーバー)
  • 「%」(パーセント)
  • 「〇」(漢数字の零)
です。

前半2つはなんてことはありません
単純にク「ー」リングが「任意の一文字」か「任意の文字列」にヒットしているだけです。
探していた問題の文字は漢数字の零だった,ということになりますね。

しかし,この検証には一つ問題がありまして,

クエリが先頭の100結果セットしか画面表示しない

ことです。
このため,100以上の結果セットでは,結果の件数しかわからないんですね。
(この場合は返ってくるかこないかがわかればよいので,さほど問題にはなりませんが)
よって,最初のクエリを投げた後,結果が返ってきている大まかな場所にあたりをつけて
もうすこし限定したクエリを投げます。
結果セットの表示文字列の 25000 行あたりに「(1行処理しました)」がありますから,問題は12000ぐらいだろうと予想するわけです。

本来必要な部分だけを取得する
DECLARE @count int
DECLARE @limit int
DECLARE @CMD nvarchar(1000)
 
SET @count = 12200
SET @limit = @count + 100
 
WHILE @count < @limit
BEGIN
SET @count = @count + 1
 
SET @CMD = 'SELECT [DATA],'
    + CAST(@count AS nvarchar) +
    ' AS [char] FROM [TestDB].[dbo].[JpnTest] WHERE [DATA] LIKE ''%ク'
    + nchar(@count) + '%'' COLLATE Japanese_CI_AS'
 
EXEC sp_Executesql @CMD
 
END
GO

こうすることで,問題の部分の結果を得ることができました。

このように,わりとシンプルなアイデアを実現することによって,実現できる部分が多くあると思います。
みなさんもぜひトライしてみてください。

この記事は Microsoft SQL Server 2005 CTP June
に基づいて記述しています。

Published 2005年10月2日 22:24 投稿者 timberlandchapel

コメント

# SQL Server 2005 Sep CTP: 文字列処理の Bug Details: Japanese_CI_ASなどで長音記号(ー)を検索できない問題@ 2005年10月4日 15:25

SQL Server 2005 Sep CTP: 文字列処理の Bug Details: Japanese_CI_ASなどで長音記号(ー)を検索できない問題

# SQL Server 2005 Sep CTP: 文字列処理の問題@ 2005年10月4日 15:29

SQL Server 2005 Sep CTP: 文字列処理の問題

# 【SQL Server 2005】日本語長音の問題はより大きな問題に@ 2005年10月4日 19:07

先日,ブログに書いた日本語長音「ー」の問題ですが,
http://blogs.timberlandchapel.com/blogs/timberlandchapel/archive/2005/10/02/443.aspx...

# SQL Server:日本語の一致の問題@ 2005年10月5日 20:57

河端さんのBlogより。
SQL Server 2005&amp;nbsp; Sep. CTP での問題です。
Feedback : 'ああ' Like 'あ★%' で、1万文字以上に一致する件

...