[英]Index for an ORDER BY which includes a “condition”
我对一个20M行表进行查询,内容如下:
ORDER BY (language_code = '%s') DESC, (language_code = '%s') DESC
%s
在运行时被实际的语言代码替换(目的是对结果进行排序,以使使用用户语言的结果排在第一位,然后使用默认语言的结果,最后使用其他语言)。
我创建了以下索引:
CREATE INDEX 'index_on_language_code' ON 'my_table' (language_code)
但是,考虑到没有ORDER BY
子句,查询只需要几毫秒,因此查询现在大约需要10秒钟,这实在太多了。
有什么更好的索引建议吗?
更新:
=> EXPLAIN for: SELECT "localized_skills".* FROM "localized_skills" ORDER BY (localized_skills.language_code = 'it') DESC, (localized_skills.language_code = 'en') DESC LIMIT 10
QUERY PLAN
Limit (cost=643126.40..643126.43 rows=10 width=42)
-> Sort (cost=643126.40..678294.56 rows=14067262 width=42)
Sort Key: (((language_code)::text = 'it'::text)), (((language_code)::text = 'en'::text))
-> Seq Scan on localized_skills (cost=0.00..339137.93 rows=14067262 width=42)
(4 rows)
更新2
在我的情况下,在ORDER BY
(或等效的解决方案)之前添加WHERE language_code = 'it' OR language_code = 'en'
并不能改善查询。 事实上,我的数据, 此刻 ,已经是唯一的连接,或者它。 当我将使用其他语言在数据库中添加更多行时,这将避免增加时间,但是查询不会少于10秒。
您的索引无法在此ORDER BY
。 如果您有固定的字符串,则可以在例如language_code = 'it'
上创建函数索引,但是在这种情况下,我建议您使用WHERE language_code = 'it' OR language_code = 'en'
执行第一个查询,对这部分查询进行排序,然后执行与所有其他语言的合并,无须顺序。 您将得到相同的结果,但我认为速度要快得多。
SELECT "localized_skills".*
FROM "localized_skills"
ORDER BY (localized_skills.language_code = 'it') DESC,
(localized_skills.language_code = 'en') DESC
LIMIT 10
该查询不包含WHERE
子句。 这意味着将读取整个表,并且在缺少LIMIT
子句的情况下,将其返回到结果集中。 LIMIT 10
在排序后的最后阶段发生。 它不能阻止读取整个localized_skills
表。
由于ORDER BY
子句中的条件,RDBMS无法使用索引。 它可能会创建一个临时表并在其中存储行,也许会动态创建索引以能够以正确的顺序输出行。 我不知道细节,我没有使用PostgreSQL
但这是MySQL
工作方式,实际上,没有办法使其运行得比这快。
您是否真的需要使用查询,而没有WHERE
子句? 添加WHERE
子句会缩小处理的行集。
一个简单的想法(无论是否添加WHERE
子句)都是将您的查询拆分为两个查询,这些查询将条件移到WHERE
子句中(在该子句中可以将这些条件与索引一起使用,以大大减少已处理的行数)。
第一个查询最多选择10条具有所需语言代码的行:
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code IN ('it', 'en')
ORDER BY (localized_skills.language_code = 'it') DESC,
(localized_skills.language_code = 'en') DESC
LIMIT 10
如果第一个查询返回的行数少于10行,则可以运行第二个查询以选择不具有所需语言代码的项的剩余数量:
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code NOT IN ('it', 'en')
LIMIT 10 # Put a lower value here if needed
对于第二个查询,不再需要按language_code
对行进行排序(两个条件均为FALSE
); 这使PostgreSQL
从表中选择第一行,并阻止它读取整个表。
您甚至可以使用UNION
组合这两个查询:
(
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code IN ('it', 'en')
LIMIT 10
UNION
SELECT "localized_skills".*
FROM "localized_skills"
WHERE localized_skills.language_code NOT IN ('it', 'en')
LIMIT 10
)
ORDER BY (localized_skills.language_code = 'it') DESC,
(localized_skills.language_code = 'en') DESC
LIMIT 10
同样,我不了解PostgreSQL
,这是使用MySQL
实现结果的正确方法。 我希望它可以帮助您使用PostgreSQL
语法和功能构造正确的查询。
ORDER BY
子句从第一个内部查询移到UNION
因为MySQL
不会保留两个内部查询检索到的顺序或行。 需要在内部查询上使用LIMIT 10
子句,以避免扫描整个表。 外部LIMIT 10
子句仅对排序后的前10行进行保留。
https://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
节目
在某些情况下,MySQL无法使用索引来解析ORDER BY,尽管它仍然使用索引来查找与WHERE子句匹配的行。 这些情况包括:
You use ORDER BY on nonconsecutive parts of a key: SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2;
这就是你在做什么。
手册中的建议是
为了提高ORDER BY的速度,请检查是否可以让MySQL使用索引而不是额外的排序阶段。 如果这不可能,则可以尝试以下策略:
增加sort_buffer_size变量值。
增加read_rnd_buffer_size变量值。
通过仅声明与保留存储在其中的值所需大小相同的列,每行使用较少的RAM。 例如,如果值不超过16个字符,则CHAR(16)优于CHAR(200)。
更改tmpdir系统变量,使其指向具有大量可用空间的专用文件系统。 变量值可以列出以循环方式使用的几个路径。 您可以使用此功能将负载分散到多个目录中。 路径应在Unix上用冒号(“:”)分隔,在Windows,NetWare和OS / 2上应用分号(“;”)分隔。 路径应命名位于不同物理磁盘上的文件系统中的目录,而不是同一磁盘上的不同分区。
或者,可以通过
{query}
WHERE language_code = '%s'
UNION
{query}
WHERE language_code = '%s'
UNION
{query}
WHERE language_code NOT IN( '%1$s', '%2$s')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.