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

但问题是这个查询很慢,似乎没有使用索引来加速第一次查询。关于如何优雅地使第二个查询(非 az)更快的任何建议?

我正在使用带有 rails 3.2 的 Postgres 9.1

谢谢

4

2 回答 2

3

在这里更新
了前面的问题的答案。

我的第一个想法(带有 的索引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 回答