ORDER BY的索引,包括“条件”

时间:2015-04-28 14:07:10

标签: sql performance postgresql indexing

我在20M行表上查询了以下内容:

ORDER BY (language_code = '%s') DESC, (language_code = '%s') DESC

%s在运行时被替换为实际的语言代码(目的是对结果进行排序,以便用户语言中的那些首先排序,然后我们使用默认语言,最后是其他语言)。 / p>

我创建了以下索引:

CREATE INDEX 'index_on_language_code' ON 'my_table' (language_code)

然而,现在查询大约需要10秒,这太过分了,考虑到没有ORDER BY子句,它只需要几毫秒。

有关更好索引的任何建议吗?

更新:

=> 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

WHERE language_code = 'it' OR language_code = 'en'(或等效解决方案)之前添加ORDER BY并不会改善我的查询。事实上,我的数据目前,已经只是en或者它。这样可以防止在我用其他语言在数据库中添加更多行时增加时间,但查询时间不会少于10秒。

3 个答案:

答案 0 :(得分:2)

您的索引在此ORDER BY中无法使用。如果您有固定的字符串,您可以创建功能索引,例如language_code = 'it',但在这种情况下,我建议您使用WHERE language_code = 'it' OR language_code = 'en'执行第一个查询命令这部分查询,而不是执行与所有其他语言的联合而不执行命令。你会有相同的结果,但我想得更快。

答案 1 :(得分:0)

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行。

答案 2 :(得分:0)

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,只需将列声明为保存存储在其中的值所需的大小。例如,CHAR(16)是   如果值永远不超过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')