web-dev-qa-db-ja.com

PostgreSQLは「アクセントを区別しない」照合をサポートしていますか?

Microsoft SQL Serverでは、(データベース、テーブル、または列に対して)「アクセントを区別しない」照合を指定できます。つまり、次のようなクエリが可能です。

SELECT * FROM users WHERE name LIKE 'João'

名前がJoaoの行を見つけます。

naccent_string contrib関数を使用してPostgreSQLの文字列からアクセントを取り除くことができることは知っていますが、PostgreSQLはこれらの「アクセントに依存しない」照合をサポートしているので上記のSELECTが機能するのでしょうか。

81
Daniel Serodio

そのために unaccent module を使用します。これはリンク先とはまったく異なります。

unaccentは、語彙素からアクセント(発音区別符号)を削除するテキスト検索辞書です。

以下を使用して、データベースごとに1回インストールします。

_CREATE EXTENSION unaccent;
_

次のようなエラーが表示された場合:

エラー:拡張制御ファイル "/usr/share/postgresql/9.x/extension/unaccent.control"を開けませんでした:そのようなファイルまたはディレクトリはありません

この関連する回答の指示に従って、データベースサーバーにcontribパッケージをインストールします。

とりわけ、サンプルで使用できるunaccent()関数を提供します(LIKEは必要ないようです)。

_SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');
_

インデックス

その種類のクエリにインデックスを使用するには、 式のインデックス を作成します。 ただし、、Postgresはインデックスに対してIMMUTABLE関数のみを受け入れます。関数が同じ入力に対して異なる結果を返すことができる場合、インデックスはサイレントにブレークする可能性があります。

unaccent() only STABLE not IMMUTABLE

残念ながら、unaccent()STABLEではなくIMMUTABLEのみです。 pgsql-bugsのこのスレッド によると、これはthreeの理由によるものです:

  1. 辞書の動作に依存します。
  2. この辞書への固定接続はありません。
  3. したがって、現在の_search_path_にも依存しますが、これは簡単に変更できます。

一部のチュートリアル Webで、関数のボラティリティをIMMUTABLEに変更するように指示します。このブルートフォース方式は、特定の条件下で機能しなくなる可能性があります。

simple IMMUTABLE wrapper関数 (過去に自分でやったように)を提案する人もいます。

2つのパラメーターを持つバリアントIMMUTABLEを使用する辞書を明示的に宣言するかどうかについては、継続的な議論があります。 here または here を読んでください。

別の代替手段は、Githubで提供される MusicbrainzによるIMMUTABLE unaccent()関数 を持つこのモジュールです。自分でテストしていない。私はより良いアイデアを思いついたと思う:

今のところベスト

私は、少なくとも他のソリューションと同じくらい効率的ですが、より安全なアプローチを提案します:2パラメータ形式と「ハード関数と辞書のスキーマ:

_CREATE OR REPLACE FUNCTION public.f_unaccent(text)
  RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1)  -- schema-qualify function and dictionary
$func$  LANGUAGE sql IMMUTABLE;_

publicは、拡張機能をインストールしたスキーマです(publicがデフォルトです)。

以前は、関数に_SET search_path = public, pg_temp_を追加していました-辞書もスキーマで修飾できることを発見するまで、 これは現在(10ページ)文書化されていません です。このバージョンは、pg 9.5およびpg 10でのテストでは少し短く、約2倍高速です。

IMMUTABLEで宣言された関数は、それを許可するために本体で不変ではない関数を呼び出さない可能性があるため、更新バージョンでは function inlining が許可されていません。このIMMUTABLE関数でexpression indexを使用している間は、パフォーマンスにはほとんど問題ありません。

_CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
_

クライアントプログラムのセキュリティは、Postgres 10.3/9.6.8などで強化されています。インデックスで使用するときに示されるように、関数と辞書をスキーマ修飾するためにneedを使用します。見る:

インデックスに一致するようにクエリを調整します(クエリプランナーが使用できるように)。

_SELECT * FROM users
WHERE  f_unaccent(name) = f_unaccent('João');
_

適切な式に関数は必要ありません。 _'Joao'_のようなアクセントのない文字列を直接指定できます。

合字

Postgres9.5またはそれ以前では、「Œ」や「ß」などの合字を手動で展開する必要があります(必要な場合)unaccent()は常にsingle文字を置換します。

_SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
E A e a S
_

このアップデートはunaccentに更新されます Postgresで9.6

_contrib/unaccent_の標準_unaccent.rules_ファイルを拡張して、Unicodeで認識されているすべての発音区別符号を処理し、合字を正しく展開します(Thomas Munro、LéonardBenedetti)

大胆な強調鉱山。今、私たちは得る:

_SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
OE AE oe ae ss
_

パターンマッチング

LIKEまたはILIKE任意のパターンで、これをモジュールと組み合わせます _pg_trgm_ PostgreSQL 9.1以降。トライグラムGIN(通常は望ましい)またはGist式インデックスを作成します。 GINの例:

_CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);
_

次のようなクエリに使用できます。

_SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');
_

GINインデックスとGistインデックスは、プレーンなbtreeよりも保守が高価です。

左に固定されたパターンには、より簡単なソリューションがあります。パターンマッチングとパフォーマンスの詳細:

_pg_trgm_は、有用な 「類似性」(_%_)および「距離」(_<->_) の演算子も提供します。

Trigramインデックスは、_~_などの単純な正規表現もサポートしています。および大文字と小文字を区別しないILIKEとのパターンマッチング:

174

いいえ、PostgreSQLはその意味で照合をサポートしていません

PostgreSQLは、そのような照合(アクセントを区別するかどうかに関係なく)をサポートしません。これは、物がバイナリに等しい場合を除き、比較が同等を返すことができないためです。これは、内部的にハッシュインデックスのようなものに多くの複雑さを導入するためです。このため、厳密な意味での照合は順序付けにのみ影響し、平等には影響しません。

回避策

Unaccentsが語彙を与える全文検索辞書。

FTSの場合、unaccentを使用して独自の辞書を定義できます。

CREATE EXTENSION unaccent;

CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
  ALTER MAPPING FOR hword, hword_part, Word
  WITH unaccent, simple;

その後、機能インデックスを使用してインデックスを作成できます。

-- Just some sample data...
CREATE TABLE myTable ( myCol )
  AS VALUES ('fóó bar baz'),('qux quz');

-- No index required, but feel free to create one
CREATE INDEX ON myTable
  USING Gist (to_tsvector('mydict', myCol));

非常に簡単にクエリできるようになりました

SELECT *
FROM myTable
WHERE to_tsvector('mydict', myCol) @@ 'foo & bar'

    mycol    
-------------
 fóó bar baz
(1 row)

こちらもご覧ください

それ自体は不明瞭。

unaccent module は、FTS統合なしで単独で使用することもできます。そのためには、 Erwin's answer

3
Evan Carroll

PostgreSQLは、照合のために基盤となるオペレーティングシステムに依存していると確信しています。 Itdoesサポート 新しい照合の作成 、および 照合のカスタマイズ 。ただし、どれだけの作業があなたに役立つかはわかりません。 (かなり多くなる可能性があります。)