5

私は現在次のものを持っています:

User (id, fname, lname, deleted_at, guest)

fname次のように、ユーザーのイニシャルでユーザーのリストを照会できます。

User Load (9.6ms)  SELECT "users".* FROM "users" WHERE (users.deleted_at IS NULL) AND (lower(left(fname, 1)) = 's') ORDER BY fname ASC LIMIT 25 OFFSET 0

これは、次のインデックスのおかげで高速です。

  CREATE INDEX users_multi_idx
  ON users (lower(left(fname, 1)), fname)
  WHERE deleted_at IS NULL;

私が今やりたいのは、文字のAZで始まらないすべてのユーザーを照会できるようにすることです。私はこれをそのように機能させました:

SELECT "users".* FROM "users" WHERE (users.deleted_at IS NULL) AND (lower(left(fname, 1)) ~ E'^[^a-zA-Z].*') ORDER BY fname ASC LIMIT 25 OFFSET 0

しかし、問題は、このクエリが非常に遅く、最初のクエリを高速化するためにインデックスを使用していないように見えることです。2番目のクエリ(az以外)をエレガントに高速化する方法についての提案はありますか?

Rails3.2でPostgres9.1を使用しています

ありがとう

4

2 に答える 2

3

更新された回答
ここの前の質問。

私の最初のアイデア (index with text_pattern_ops) は、テストの正規表現では機能しませんでした。クエリを次のように書き直してください。

SELECT *
FROM   users
WHERE  deleted_at IS NULL
WHERE lower(left(fname, 1)) < 'a' COLLATE "C"
OR    lower(left(fname, 1)) > 'z' COLLATE "C"
ORDER  BY fname
LIMIT  25 OFFSET 0;

これらの式が一般的に高速であることに加えて、正規表現にも大文字が含まれていたため、インデックスと一致しませんでしたlower()。また、単一の文字と比較すると、末尾の文字は無意味でした。

そして、このインデックスを使用します:

CREATE INDEX users_multi_idx
ON users (lower(left(fname, 1)) COLLATE "C", fname)
WHERE deleted_at IS NULL;

このCOLLATE "C"部品はオプションであり、パフォーマンスのごくわずかな向上にのみ貢献します。目的は、照合ルールをデフォルトの posix 照合にリセットすることです。これは、バイト順を使用するだけで、一般的に高速です。とにかく照合規則が関係ない場合に便利です。

それを使用してインデックスを作成すると、照合に一致するクエリのみがそれを使用できます。したがって、パフォーマンスが最重要要件でない場合は、単純化するためにスキップすることもできます。

于 2012-10-16T01:44:49.230 に答える
2

@ErwinBrandstetter の一般的な解決策の代わりに、PostgreSQL は部分インデックスをサポートしています。あなたは言うことができます:

CREATE INDEX users_nonalphanumeric_not_deleted_key
ON users (id)
WHERE (users.deleted_at IS NULL) AND (lower(left(fname, 1)) ~ E'^[^a-zA-Z].*');

このインデックスは他のルックアップには役立ちませんが、この特定のクエリに対する回答を事前に計算します。結果として得られるインデックスはテーブルの大部分を無視し、対象の行のみを含むため、この手法は、非常に大きなテーブルから小さな事前定義されたサブセットを返すクエリに役立つことがよくあります。

于 2012-10-16T03:56:51.387 に答える