我目前有以下内容:
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;
我现在想要做的是能够查询所有不以字母 A-Z 开头的用户。我让它像这样工作:
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
但问题是这个查询很慢而且似乎没有使用索引来加速第一个查询。关于如何优雅地使第二个查询(非 a-z)更快的任何建议?
我正在使用带有 Rails 3.2 的 Postgres 9.1
谢谢
最佳答案
更新的答案
Preceding question here.
我的第一个想法(索引为 text_pattern_ops
)在我的测试中不适用于正则表达式。最好将您的查询重写为:
SELECT *
FROM users
WHERE deleted_at IS NULL
<b>WHERE lower(left(fname, 1)) < 'a' COLLATE "C"
OR lower(left(fname, 1)) > 'z' COLLATE "C"</b>
ORDER BY fname
LIMIT 25 OFFSET 0;
除了这些表达式通常更快之外,您的正则表达式中还包含大写字母,这与 lower()
的索引不匹配。与单个字符相比,尾随字符毫无意义。
并使用这个索引:
CREATE INDEX users_multi_idx
ON users (lower(left(fname, 1)) <b>COLLATE "C"</b>, fname)
WHERE deleted_at IS NULL;
COLLATE "C"
部分是可选的,只会带来非常小的性能提升。它的目的是将排序规则重置为默认的 posix 排序规则,它只使用字节顺序并且通常更快。在归类规则无论如何都不相关的情况下很有用。
如果您使用它创建索引,则只有与排序规则匹配的查询才能使用它。因此,如果性能不是您的首要要求,您可以跳过它以简化事情。
关于ruby-on-rails - 更快地搜索字段的第一个字符与 [A-Za-z] 不匹配的记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12906264/