在进行分析查询时如何避免 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

如果该组由idval 组成

select
    id, val, max(ts)
  from
    x
  group by id, val
  order by id, val

所以我不会使用将聚合放在子查询中(可能会更慢) 我也不会使用窗口聚合函数(因为你可以用普通的group bymax 来做) 我不会使用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 查询中进行分页

避免重复数据查询DISTINCT

mysql 数据操作 单表查询 简单查询 避免重复DISTINCT

SQL中distinct的用法(四种示例分析)

使用count distinct在postgres中进行慢速查询

Hive SQL优化之 Count Distinct