繁体   English   中英

ORDER BY的索引,其中包含“条件”

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM