在进行分析查询时如何避免 DISTINCT 作为拐杖?
Posted
技术标签:
【中文标题】在进行分析查询时如何避免 DISTINCT 作为拐杖?【英文标题】:How to avoid DISTINCT as a crutch when doing analytic queries? 【发布时间】:2013-06-06 04:14:34 【问题描述】:我有一个查询,我认为它具有相当普遍的模式。考虑这张表:
id | val | ts
---+-----+-------
a | 10 | 12:01
a | 12 | 12:05
a | 9 | 12:15
b | 30 | 12:03
我想通过时间戳获取每个 id 的最新值。一些方法可以做到:
-- where in aggregate subquery
-- we avoid this because it's slow for our purposes
select
id, val
from t
where (id, ts) in
(select
id,
max(ts)
from t
group by id);
-- analytic ranking
select
id, val
from
(select
row_number() over (partition by id order by ts desc) as rank,
id,
val
from t) ranked
where rank = 1;
-- distincting analytic
-- distinct effectively dedupes the rows that end up with same values
select
distinct id, val
from
(select
id,
first_value(val) over (partition by id order by ts desc) as val
from t) ranked;
分析排名查询感觉像是最容易提出有效查询计划的查询。但在美学和维护方面,它非常难看(尤其是当表的值列不止 1 个时)。 在生产中的一些地方,当测试表明性能相当时,我们会使用独特的分析查询。
有没有什么方法可以做 rank = 1 之类的事情,而不会得到如此丑陋的查询?
【问题讨论】:
如果有像a, 10, 13:45
这样的另一行,你期望得到什么结果? (所以有些记录 id 和 val 的组合不是唯一的)。
@Beryllium 我提出的所有 3 个查询都应该按时间戳选择最新值。因此,如果将您的行添加到示例表中,它应该可以很好地处理它。如果同一值有 2 个相同的时间戳,则会导致聚合查询出现问题。
“拐杖”+1! Distinct 是最广泛使用的消除重复项的工具,但实际上查询存在问题。有合法的用途,但对我来说,每当我看到它时,它都会在查询中显示一个危险信号。
mysql 有一个巧妙的解决方法。您只想查看 postgres 答案吗?
【参考方案1】:
如果您仅按id
分组
select
id, max(ts)
from x
group by id
order by id
如果该组由id
和val
组成
select
id, val, max(ts)
from
x
group by id, val
order by id, val
所以我不会使用将聚合放在子查询中(可能会更慢)
我也不会使用窗口聚合函数(因为你可以用普通的group by
和max
来做)
我不会使用distinct
,因为这意味着不同的东西(至少对我而言)。
如果你在id
上进行分组,并且想要一个 val
的值,我建议使用窗口聚合函数,因为你必须以某种方式定义哪个 em> val
选择:而这个意图属于order by
紧跟在partition by
之后。
从维护的角度来看,我认为窗口聚合函数真正描述了您的意图 - 您想要实现的目标。其他查询以某种方式隐藏了他们的意图。就个人而言,当我阅读您的查询时,第二个是最容易理解的。
从性能的角度来看,我可以确认窗口聚合很快(至少在我的情况下)。优化器也可能从语法中受益。
【讨论】:
【参考方案2】:这是最简单最快的:
select distinct on (id)
id, ts, val
from t
order by id, ts desc
distinc on
(仅限 Postgresql)将为每个 id 返回一行。使用order by
,您可以控制哪一个。在这种情况下,最后一个ts
。使用distinct on
,您可以根据需要在结果集中包含任意数量的列,而无需中间步骤。 distinct on
中使用的列必须首先包含在 order by
中。
【讨论】:
以上是关于在进行分析查询时如何避免 DISTINCT 作为拐杖?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 DISTINCT 在 NHibernate SQL 查询中进行分页
mysql 数据操作 单表查询 简单查询 避免重复DISTINCT