MS ACCESS 95/97 の美しいソート順

MS Access を使った人ならだれしも, ソートされたデータを見て 「なんでこんな順序に並ぶの? 本当に並んでるの?」 と首をひねりたくなった経験があるでしょう。 しかし, 実際にどういう順序で並ぶかをきちんと記述したものは存在しないようです。 この文章が MS ACCESS のソート順の謎の解明に少しでも役立てば幸いです。

目次:


はじめに: どうやってソート順を調べるか

あらかじめ注意しておかなければならないのは, ACCESS のソート順では, 互いに異なる文字列について, 必ずしも大小の差がないということです。 ひらがなとカタカナ, 半角と全角, 大文字と小文字などは区別しません。 したがって, ひらがなとカタカナの混じったデータは, ソートするたびに順序が変わって, まるでソート順が存在しないかのように見えることがあります。

この現象自体はそれほど奇怪ではありません。 ACCESS 以外でも, 「大文字と小文字を区別しないインデックス」 をサポートしているデータベース管理ソフトウェアは数多くあります。

しかし, MS ACCESS の場合は等しくなる場合が非常に多いこと, どうして等しくなるのか明らかでない場合が多いこと, 「大文字と小文字を区別する」というオプションが存在しない (バイナリで比較する場合は除いて) ことなど, 扱いにくさが群を抜いています。

この現象は, ソート順の解明のしにくさに直接つながります。 適当にデータを作成して, ソートしてみたからといって, その結果が大小の違いであるかどうかがわからないのです。 もう一度ソートすると違う順序で並ぶかもしれません。

ソートしただけでソート順がわからないのなら, どうやって調べればいいでしょうか。 Access Basic を使って, 大小比較演算子で調べる, という方法を試してみましょう。 まずすべての 1文字データを比較してみます。 Access ではデータの終わりにスペースを入れることができませんから, 比較データの終わりに「!」を付け加えて比較します(こういう場合の常套手段)。 すると, こういう結果になりました。

いきなり推移律が成り立たない! ううーん。 この方法はだめみたいです…というか, これじゃどんな方法でもダメなんじゃないかと目の前がまっくらになりました。

気を取り直して, 上記データをソートしてみました。 すると不思議。 こんどは「−!」よりも「ー!」の方が大きくなります。 大小比較演算子にバグがあるのか, 比較の順序とソートの順序が違うのか…。

とにかく Access Basic は使えないということがわかったので, まずソートしてみた結果に SELECT DISTINCT の QUERY をかけて同値データを取り除く, という手間のかかる方法で調査することにしました。


清音と濁音, 直音と拗音, 長音, 踊り字

日本語のソート順で問題になるのは濁点・長音・拗音などの処理ですが, ACCESS はなかなかがんばっています。 大まかに下のようなアルゴリズムを使って並べています。

  1. まず, アクセント記号・清濁や文字の大小, 踊り字を使っているかを無視して比較する。 ハイフン文字(後述)は無視する。
  2. それで大小の決着がつかなければアクセント記号を考慮に入れる。 アクセントがついているものはついていないものより大きいと考える。
    例: AA < ÅA < AB; е < ё < ев
  3. それで大小の決着がつかなければ清音 < 濁音 < 半濁音の順に並べる。
    例: はか < はが < ばか < ばが < はかり
  4. それでも決着がつかなければ小さい字 < 大きい字の順に並べる。
    例: きょし < きよし < きょじ
  5. それでも決着がつかないときは,仮名踊り字・長音を使っているかどうかで比較する。 使ってない場合より,使っている場合の方が大きい。
    例: ききょ < きゝょ < ききよ
  6. それでも決着がつかないときは,ハイフン文字を考慮に入れて比較する。
  7. それでも決着がつかないときは,同値とみなす。 同値のデータをソートした場合, どちらが先にくるかは不定となる。

「濁音仮名」と「清音仮名+濁点」は同じとみなします(例: 「び」=「ひ」+「゛」)。 ここでおもしろいのは, 「濁点・半濁点はかな以外にもつけられる」ということです。 さらに濁点を複数つけることもできます。 半濁点 1個は(なぜだか知りませんが)濁点 2個ぶんと同じとみなします (例: 山あ < 山゛あ < 山゛゛あ = 山゜あ < 山゛゜あ < 山い)。

もっとすごい話。 「℃」は C に半濁点のついたものとみなします。 ウソだと思ったら「℃」をデータに入れて, 「C゜」で検索してご覧なさい。

濁点については他にもいろいろおもしろい話がありますが後回しにします。


無視される違い, 無視される文字

先に述べた「無視される違い」ですが, 次のようなものがあります。

以上のうち, 全角と半角の違いについては, JIS X 0201 と JIS X 0208 の記号について, どれとどれが対応しているのかが自明ではないので, とくに注意すべき対応関係を以下に書いておきます。

半角全角 備考
20 8140 SPACE, JIS規格では全角と半角の差とはしていない
22 FA57 QUOTATION MARK, FA57 は IBM拡張文字
27 FA56 APOSTROPHE, FA56 は IBM拡張文字
2D 817C HYPHEN-MINUS, JIS規格では全角と半角の差とはしていない
5C 818F YEN SIGN
60 814D GRAVE ACCENT
7E 8160 TILDE, JIS規格では全角と半角の差とはしていない

以上のほかに, 「いくつあっても無視される文字」というのがあります。 典型的には JIS X 0208 の未定義領域にある字がそうです (F040-F9FC のユーザー定義文字は無視しません) が, それ以外に以下の文字があります(もちろん対応する半角の濁点も同様)。

文字 SJIS説明
814A 濁点(前に文字がない場合)
814B 半濁点(前に文字がない場合)
815A 漢数字のゼロ
81FC 合成用丸

ACCESS では「一」も「一〇〇〇〇」も同じになってしまうのです!!

合成用丸を無視するのはいいとして, 漢数字のゼロを無視するのは (いくら形が似ているからといって) 合点が行きません。

なお, 「無視する」といいましたが, 空文字列よりは大きいことになっています。 これだけ例外です。


ハイフン文字について

以下の 4 文字(全角と半角を別に数えれば 6文字) を本文章では「ハイフン文字」と呼ぶことにします。

文字SJIS説明
80 80H
' 27, FA56 APOSTROPHE
- 2D, 817C HYPHEN-MINUS, MINUS SIGN
815D HYPHEN

ハイフン文字は上記の「◯」などと同様たいてい無視されるのですが, ハイフン以外の部分が同値の場合, 以下のようなきわめて複雑な方法で比較します。

  1. まず両方の文字列の先頭から, (ハイフン文字を含めて)一致している部分を取り除く。 全部一致していたら文字列は等しいものとみなす。
  2. 片方の文字列がハイフン文字をまったく含んでいない場合, ハイフン文字を含んでいる方よりも小さいとみなす。
    例: its < it's
  3. 両方の文字列がハイフン文字を含んでいるが, ハイフン文字が最初に出現する場所が違う場合, 後ろに出てきた方が大きいとみなす。
    例: a-stray < as-tray
  4. 両方の文字列でハイフン文字の最初の出現個所が等しい場合, 上に列挙した順序(80H < アポストロフィ < マイナス < ハイフン) の順に並ぶ。

これを効率よく実装するのはけっこう頭の体操になりますよ。


記号の並び順と Unicode と濁音パート2

なんだかよくわからないタイトルですが, 記号の並び順についてです。

記号は(上記の無視される文字を除いて)英数字や仮名・漢字などより小さいものとみなします。 問題は記号どうしをどういう順序で並べるかですが, これがなかなか一筋縄では行きません。 本文章では便宜上, 記号を 「記述記号1・記述記号2・数学記号・幾何学図形・矢印・罫線記号・一般記号・飾り文字」 の 8類(この順に並ぶ)に分けて説明します。

記述記号1

53文字あります。下のようにおおむね Unicode 順に並びます。 コードをカッコでくくってあるのは例外です。

Unicode002000210022002300240025002600280029002a
文字 ' ' ! "# $ % &( ) *
Unicode002c(3001)002e(3002)002f003a003b003f0040005b
文字 , . / : ; ? @ [
Unicode005c 005d 005e 005f 0060007b007c007d007e00a6
文字 ] ^ _ ` { | } ~ U
Unicode00a800af 00b4 2018 2019201c201d203220333008
文字 ¨ ´
Unicode3009300a 300b 300c 300d300e300f301030113014
文字
Unicode3015301d 301f
文字

区点(。)がピリオド(.)の次に並ぶこと, 読点(、)がコンマ(,)の次に並ぶこと, 005c が円記号でなくて「\」であることに注意。

記述記号2

以下の 2記号がこの順に並びます。

文字 SJIS Unicode 説明
81563003 同じく記号
81593006 しめ

数学記号

43文字。やはり「おおむね」 Unicode の順に並ぶのですが, 00B1 と 00AB の順序だけ逆になっています。

Unicode 002b003c003d003e00b1(00ab)00bb00d700f72200
文字 + < = > ± × ÷
Unicode2202220322072208220b2211221a221d221f2220
文字
Unicode2225222722282229222a222b222c222e22342235
文字
Unicode223d225222602261226622672282228322862287
文字
Unicode 22a5 22bf 2312
文字

幾何学図形

11文字あります。 やはり Unicode 順に並ぶのですが, 同じ形の図形(四角・三角・逆三角・ひし形・丸) のバリアントどうしは, Unicode の値の差だけ濁点をつけたものとみなします。 (例: ■゛=□, ◎゛=●, ○゛゛゛ =◎) なんでだー!!

Unicode25a025a125b225b325bc25bd25c625c725cb25ce25cf
文字

矢印

6文字あります。 Unicode 順でなく, 時計まわり(?)に並ぶようです。 ただし,右二重矢印(⇒)は右矢印(→)より濁点14個ぶん!! 大きいものに等しいとみなします。 (⇒ = →゛゛゛゛゛゛゛゛゛゛゛゛゛゛)

なんでか知りません。 もちろん濁点14個のかわりに半濁点7個でもかまいません。

Unicode2191219221d22193219021d4
文字

罫線記号

32文字あります。 Unicode 順に並びますが, 例によって罫線の太さの差は,濁音と同じように処理します。 すなわち,細い罫線に対して太い罫線は Unicode の値の差のぶんだけ濁点がついたものとみなします。 (例: ─ < ─゛= ━ < ─#; ┌ < ┌ ゛< ┌゛゛< ┌゛゛゛= ┏ < ┌#)

Unicode2500250125022503250c250f2510251325142517
文字
Unicode2518251b251c251d25202523252425252528252b
文字
Unicode252c252f25302533253425372538253b253c253f
文字
Unicode2542254b
文字

一般記号

16文字あります。 以下の 3点の解釈をほどこすによって, Unicode 順にならんでいるものとみなすことが可能です。

  1. 円記号:\ (5c)は Unicode では 00a5 である
  2. 「・」はカタカナではなく MIDDLE DOT (00b7) と解釈する
  3. 下の 5文字は本来みな無効な文字だが, 上記「・」と同値だと評価する。 おそらく Access のバグと思われる。 (SJIS: 8540 8640 eb40 ec40 ef40)
Unicode00a200a300a500a700ac00b000b600b720202021
文字 ¢ £ § ¬ °
Unicode202520262030203b30123013
文字

飾り文字

7文字あります。 おおむね Unicode 順に並んでいますが, なぜか★と☆の順だけひっくり返っています。

Unicode2606260526402642266a266d266f
文字

なんでこう細かい違いがあるんでしょうね。


英数ギリシャキリル文字・仮名・漢字の並び順と合字

数字

41字あります。 数字は値の順に並びます。 同じ値を持つ数字は,算用数字 < 丸付き数字 < ローマ数字の順にならびます。 算用数字の全角と半角, ローマ数字の大文字と小文字は区別しません。 丸付き数字は算用数字にアクセント記号をつけたものと考えます。 (例: 1 < 1゜< @ < 1! < T) 一番大きいのは「∞」です(笑)。 なにもここまで凝らなくても…。

注意: 比較はあくまで文字単位で行うので, 文字列をソートしても数値の順にはなりません。

Unicode0030003124602160003224612161003324622162
文字 0 1 @ T 2 A U 3 B V
Unicode0034246321630035246421640036246521650037
文字 4 C W 5 D X 6 E Y 7
Unicode2466216600382467216700392468216824692169
文字 F Z 8 G [ 9 H \ I ]
Unicode246a246b246c246d246e246f2470247124722473
文字 J K L M N O P Q R S
Unicode221e
文字

ラテンアルファベット

38字あります。 アルファベット順に並びます。 アクセント記号つきアルファベットや合字もここに入ります。 大文字と小文字, 全角と半角は区別しません。

先に述べたとおり, 「Å」は「A」にアクセントがついた文字, 「℃」は C に半濁点のついた文字とみなします。 (例: AA < ÅA < AB)

それ以外の字については文字単位の比較であって, 文字列が必ずしもアルファベット順に並ぶわけではありません。 (例: c < cz < t {合字} < d)

Unicode0041(212b)00420043(2103)(33c4)(339d)004400450046
文字 A B C t p D E F
Unicode004700480049004a004b(338f)(33cd)(339e)004c004d
文字 G H I J K s q L M
Unicode(338e)(339c)004e(2116)004f0050005100520053
文字 u r o n Y O P Q R S
Unicode0054(2121)00550056005700580059005a
文字 T Z U V W X Y Z

ギリシャ文字

アルファベット順に並びます。 大文字と小文字は区別しません。

キリール(ロシア)文字

原則としてアルファベット順に並びます。 大文字と小文字は区別しません。 ただし, 「е」と「ё」はアクセント記号の有無の違いとみなします。 (例: е < ё < ев)

仮名

五十音順に並びます。 字の大小・清音と濁音などについては先に述べたとおりです。 なお, 「小さな字」には「ヵ」「ヶ」を含みます。

仮名合字

シフトJISコード順に並びます。

SJIS875f 8760 8761 8762 8763 8764 8765 8766 8767 8768
文字ミリキロセンチメートルグラムトンアールヘクタールリットルフット
SJIS8769876a876b876c876d876e
文字カロリードルセントパーセントミリバールページ

漢字

原則として, シフトJISコード順に並びます。 「仝」および漢字の合字もここに含まれます。 拡張漢字は IBM のコード順に並びます。

例外として, 丸付き漢字・括弧付き漢字は丸を取り除いた文字の次に並びます。 (例: 右 < 右大臣 < < 宇; 株 < 株式 < X < 兜)

ソートしたとき最小の漢字は「仝」(SJIS:8157)
拡張漢字を除く最大の漢字は「熙」(SJIS:eaa4)
最初の拡張漢字は 「\」(SJIS:fa5c)
最後の拡張漢字は 「K」(SJIS:fc4b)

ユーザー定義文字(SJIS:f040-f9fc)

シフトJISコード順に並びます。

未定義の 1 バイト文字

コード a0, fd, fe の文字がこの順に並びます。


最後の大物: 長音と踊り字

長音と踊り字については先に簡単に述べましたが, もうちょっと正確に書きます。 このクラスに属する字には次の7字があります。 (注意: 「〃」は「記述記号2」に属します。また「仝」は漢字です。)

Unicode30fd30fe309d 309e 3005 30fc 2015
文字

単独で出てきた場合, 踊り字・長音符号は他のどの字よりも大きいものとみなされます。 この場合, 7字の間には大小の差はなく, 完全に同値です。

仮名の直後にこのクラスの字が来た場合,下の表のように評価されます。

長音符号仮名踊り字 漢字踊り字
(ー,―) (ヽゝ) (ヾゞ) (々)
子音 ゼロ 前字を継承 前字を継承 前字を継承
清濁 常に清音 常に清音 常に濁音 半濁音より大
母音 前字を継承 前字を継承 前字を継承 前字を継承
大きさ前字を継承 前字を継承 前字を継承 前字を継承

長音符号の直前が「ん」の場合, 長音符号は「ん」として評価されます。

長音・踊り字つきの文字列をそうでない文字列と比較した場合, 清濁・仮名の大小まで考えて,それでも決着がつかない場合,

  1. 長音・踊り字なし
  2. 踊り字
  3. 長音

の順に並びます。 例:

  1. きいゃ < きーゃ < きいや < きーや < きいゃぁ
  2. びひゃ < びヽゃ < びひや < びヽや <
    びびゃ < びヾゃ < びびや < びヾや < びぴゃ
  3. いいい < いゝい < いーい < いいい゛ < いい゛い <
    いゞい < いー゛い < いい゜い < いゝ゜い < いー゜い <
    い々い < い々゛い < い々゜い

踊り字・長音を複数ならべる事もできます。 (例: いーい < いーー < いーう)

直前の文字が非仮名の場合, 長音符号・踊り字は直前の文字 (ただし, ÅはA, ёはеに直す)を繰り返したものと完全に同値になります。 清濁については仮名の場合と同じです。 (例: Åー = ÅA, 梶R = 括, 山ゞ = 山山゛)


終わりに

これで私の Access ソート順の巨大な仕様の解析を終わりますが, どなたかエミュレートするプログラムを作ってみませんか?

実はあまりにも煩瑣にわたるので説明を省略した部分があります。 ハイフンの後ろに踊り字がついた場合, ハイフンを繰り返したものとみなすのか, とか。 興味のある人はこのへんを試してみてください。

Windows98 で Unicode を使えば, 丸付きの「株」など, 大量の新たな合字を使用できます。 そのとき ACCESS のソート順はどうなるのでしょうか。 楽しみ, 楽しみ。


参考文献:

  1. 日本語文字列照合順番 JIS X 4061-1996. 日本規格協会.

コンピュータ関係に戻る