PostgreSQLを使っているとき、日本語がうまくソートできない。これはいわゆるCOLLATION (照合順序)の設定によります。 色々とこの順番を設定できるため、今回その設定を試してみました。
基礎知識
Postgresqlでは、それぞれのデータタイプはcollationを持っており、その値によってソート順等が制御されます。例えばa
よりもb
の方が大きいと扱う、というような定義はcollationによるものです。
Postgresqlにおけるこのcollationの定義は、providerによって与えられます。このproviderは複数存在し、よく使われるのはlibc
やicu
です。
A collation definition has a provider that specifies which library supplies the locale data.
providerとしてlibc
を使用する場合、環境変数LC_COLLATE
およびLC_CTYPE
にて実際のcollationが定義されます。一方で、icu
を使用する場合は、当該ライブラリによって定義されるcollatorの名称によってcollationが定義されます。
Postgresqlで利用できるcollationの種類は、メタコマンド\dOS+
によって参照できます。
List of collations Schema | Name | Collate | Ctype | Provider | Deterministic? | Description ------------+------------------------+------------+------------+----------+----------------+---------------------------------------------- pg_catalog | C | C | C | libc | yes | standard C collation pg_catalog | C.UTF-8 | C.UTF-8 | C.UTF-8 | libc | yes | pg_catalog | POSIX | POSIX | POSIX | libc | yes | standard POSIX collation pg_catalog | af-NA-x-icu | | | icu | yes | Afrikaans (Namibia) (略)
何も考えないでデータベースを作るとどうなるか
DockerHubにあるpostgres:15.3イメージを何も考えずに使うとCOLLATIONはどうなるのでしょうか。
このkiririmode
というデータベースを作成した結果は以下で、providerはlibc
、Collationとしてはen_US.utf8
、en_US.utf8
が選択されています。
kiririmode=> \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------------+----------+----------+------------+------------+----------------------- kiririmode | postgres | UTF8 | en_US.utf8 | en_US.utf8 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres (4 rows)
これは、template[01]
から引き継いだ設定です。
デフォルトでは、databaseを作成するとtemplate1
から設定を持ってくるようになっています。
By default, the new database will be created by cloning the standard system database template1
Collationの設定とソート順の確認
本エントリでは、以下のCollationをdatabaseに設定してみて、その結果を見ることにします。
provider | LC_COLLATE |
---|---|
libc | en_US.utf8 |
libc | C |
icu | ja-JP-x-icu |
icu | ja-x-icu |
具体的には、以下のようなSQLによって、異なるCollationを持つdatabaseをそれぞれ構築します。
CREATE DATABASE en WITH ENCODING=utf8 LC_COLLATE='en_US.utf8' TEMPLATE=template0; CREATE DATABASE c WITH ENCODING=utf8 LC_COLLATE='C' TEMPLATE=template0; CREATE DATABASE jajpxicu WITH ENCODING=utf8 LOCALE_PROVIDER=icu ICU_LOCALE='ja-JP-x-icu' TEMPLATE=template0; CREATE DATABASE jaxicu WITH ENCODING=utf8 LOCALE_PROVIDER=icu ICU_LOCALE='ja-x-icu' TEMPLATE=template0;
各databaseには、それぞれ以下のデータを入れました。
create table strs ( s varchar(10) ); insert into strs(s) values ('あいうえお'), ('かきくけこ'), ('アイウエオ'), ('カキクケコ'), ('ABCDE'), ('ABCDE'), ('abcde'), ('abcde'), ('12345'), ('12345');
結果は以下に示します。
C
のみが結果が大きく異なり、他の3つは今回のデータセットにおいては同じソート順を示しました。これら3つはいわゆる辞書順、と言えば良いでしょうか。
慣れているせいかC
が一番自然に感じます。
en_US.utf8
en=# select * from strs order by 1; s ------------ 12345 12345 abcde abcde ABCDE ABCDE あいうえお アイウエオ かきくけこ カキクケコ (10 rows)
C
c=# select * from strs order by 1; s ------------ 12345 ABCDE abcde あいうえお かきくけこ アイウエオ カキクケコ 12345 ABCDE abcde (10 rows)
ja-JP-x-icu
jajpxicu=# select * from strs order by 1; s ------------ 12345 12345 abcde abcde ABCDE ABCDE あいうえお アイウエオ かきくけこ カキクケコ (10 rows)
ja-x-icu
jaxicu=# select * from strs order by 1; s ------------ 12345 12345 abcde abcde ABCDE ABCDE あいうえお アイウエオ かきくけこ カキクケコ