在 SQL 中选择具有 MIN(计算排名)和 GROUPed BY 不同列的行时性能不佳
Posted
技术标签:
【中文标题】在 SQL 中选择具有 MIN(计算排名)和 GROUPed BY 不同列的行时性能不佳【英文标题】:Bad performance when SELECT rows with MIN(calculated rank) and GROUPed BY different column in SQL 【发布时间】:2016-09-08 23:06:07 【问题描述】:我的问题与以下类似:How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?,但我有一些具体问题。
假设我们有这样的表:
| title_id | title | type_id | crit_id |
+----------+----------+---------+---------+
| 1 | title_A1 | 0 | 1111 |
| 2 | title_A2 | 1 | 1111 |
| 3 | title_B1 | 50 | 2222 |
| 4 | title_B2 | 50 | 2222 |
| 5 | title_C1 | 72 | 3333 |
| 6 | title_C2 | 1 | 3333 |
| 7 | title_C4 | 0 | 3333 |
“title_id”是唯一的并被索引,“title”和“crit_id”被索引。
因此,我希望只有按“crit_id”分组的行,保留根据 type_id 计算的自定义排名(优先级)的最小值。 对于等式type_id 的排名如下所示:
type_id = 0 - rank = 10
type_id = 50 -rank = 11
type_id = 1 - rank = 15
type_id = 72- rank = 35
etc...
最后,所有内容都应按“标题”的字母顺序排列 根据要求结果应该是:
| title_id | title | type_id | crit_id | rank |
+----------+----------+---------+---------+------+
| 1 | title_A1 | 0 | 1111 | 10 |
| 3 | title_B1 | 50 | 2222 | 11 |
| 7 | title_C4 | 0 | 3333 | 10 |
我正在使用 SQLITE。我可以通过查询获得所需的结果:
SELECT *, MIN(CASE WHEN type_id = 0 THEN 10
WHEN type_id = 1 THEN 11
WHEN type_id = 50 THEN 15
WHEN type_id = 72 THEN 35
ELSE 1000 END) as rank
FROM titles WHERE ... GROUP BY crit_id ORDER BY title
这个查询的性能真的很差。在 1 000 000 条记录上,它的执行时间超过 10 秒。
这里有几个问题:
-
我们有大约 60% 的记录的 type_id == 0。在这种情况下,我们执行了大约 600 000 次 MIN 和 CASE 子句。由于排名是计算出来的,我们不能在这里使用索引。我想要一些如何最小化它的执行。
对如此大量的数据使用 GROUP BY 会带来非常糟糕的性能。在阅读Selecting records holding group-wise maximum (on a unique column) 之后,我不确定它是否总是有正确的行为。希望有另一种方式可以做与 Group By 类似的事情。
PS: 我在嵌入式设备上运行这个,内存卡很慢,因此访问数据库很慢。
我不是 SQL 专家,所以如果有任何解决方案,我将不胜感激。 提前致谢。
忘了说,我们可以LIMIT
应该返回的结果数量。对于等式LIMIT 500
.
【问题讨论】:
select *
和 group by
几乎从不符合作者的意图。
是要在表中排名的 typeid 映射吗?
尝试:(1)使用其他CASE
语法,您首先指定要测试的值,然后使用不同的WHEN
子句,然后(2)移动MIN
CASE
内部的函数,因此得到:CASE MIN(type_id) WHEN 0 THEN 10 WHEN ...
当相同的crit_id
值有不同的值时,title_id
和其他列应该给出什么?在 SQL 标准中(默认在 mysql 5.7 中应用)你需要以某种方式聚合title_id
(例如min(title_id)
),或者也按它分组。对于其他在功能上不是由crit_id
确定的列也是如此。
另外,在 10 秒内从存储卡中查询嵌入式设备上的 1MM 记录听起来并不“非常糟糕”。如果 I/O 或 CPU 是您的瓶颈,那么更改查询可能根本无济于事。
【参考方案1】:
也许您可以通过使用union all
拆分查询来利用(type_id, crit_id)
上的复合索引:
select crit_id, min(rank) from (
select distinct crit_id, 10 as rank
from titles where type_id = 0
union all
select distinct crit_id, 11
from titles where type_id = 1
union all
select distinct crit_id, 15
from titles where type_id = 50
union all
select distinct crit_id, 35
from titles where type_id = 72
union all
select distinct crit_id, 1000
from titles where type_id not in (0,1,50,72)
) t group by crit_id
【讨论】:
以上是关于在 SQL 中选择具有 MIN(计算排名)和 GROUPed BY 不同列的行时性能不佳的主要内容,如果未能解决你的问题,请参考以下文章
在 SQL Server 中使用 Dense_Rank 对具有排名的列进行排名组合