在不使用子查询的情况下使用 SELECT DISTINCT ON 计算总行数

Posted

技术标签:

【中文标题】在不使用子查询的情况下使用 SELECT DISTINCT ON 计算总行数【英文标题】:Counting the total number of rows with SELECT DISTINCT ON without using a subquery 【发布时间】:2018-01-09 13:15:19 【问题描述】:

我使用 PostgreSQL SELECT DISTINCT ON 语法执行了一些查询。我想让查询在每个结果行旁边返回总行数。

假设我有一张表my_table,如下所示:

CREATE TABLE my_table(
    id int,
    my_field text,
    id_reference bigint
);

然后我有几个值:

 id | my_field | id_reference 
----+----------+--------------
  1 | a        |            1
  1 | b        |            2
  2 | a        |            3
  2 | c        |            4
  3 | x        |            5

基本上my_table 包含一些版本化数据。 id_reference 是对数据库全局版本的引用。对数据库的每次更改都会增加全局版本号,并且更改总是会向表中添加新行(而不是更新/删除值),并且它们会插入新的版本号。

我的目标是执行一个只检索表中最新值以及总行数的查询。

例如,在上述情况下,我想检索以下输出:

| total | id | my_field | id_reference |
+-------+----+----------+--------------+
| 3     | 1  | b        |  2           |
+-------+----+----------+--------------+
| 3     | 2  | c        |  4           |
+-------+----+----------+--------------+
| 3     | 3  | x        |  5           |
+-------+----+----------+--------------+

我的尝试如下:

select distinct on (id)
    count(*) over () as total,
    *
from my_table
order by id, id_reference desc

这会返回几乎正确的输出,除了 totalmy_table 中的行数而不是结果查询的行数:

 total | id | my_field | id_reference 
-------+----+----------+--------------
     5 |  1 | b        |            2
     5 |  2 | c        |            4
     5 |  3 | x        |            5
(3 rows)

如您所见,它具有5,而不是预期的3

我可以通过使用子查询和count 作为聚合函数来解决这个问题:

with my_values as (
  select distinct on (id)
    *
  from my_table
  order by id, id_reference desc
)
select count(*) over (), * from my_values

这会产生我预期的输出。

我的问题:有没有办法避免使用这个子查询并让类似于count(*) over () 的东西返回我想要的结果?

【问题讨论】:

我不确定在这种情况下您是否可以避免子查询。您需要在某处获得中间结果才能使用COUNT(*) OVER () 从实际查询中获取行数。 【参考方案1】:

您正在查看my_table 3 种方式:

    为每个id查找最新的id_reference 为每个id查找最新的id_referencemy_field 统计表中ids 的不同数量

因此我更喜欢这个解决方案:

select
    c.id_count as total,
    a.id,
    a.my_field,
    b.max_id_reference
from
    my_table a
    join
    (
        select 
            id,
            max(id_reference) as max_id_reference
        from 
            my_table
        group by
            id
    ) b 
    on
        a.id = b.id and
        a.id_reference = b.max_id_reference
    join
    (
        select
            count(distinct id) as id_count
        from
            my_table
    ) c
    on true;

这有点长(尤其是我编写 SQL 的长而细的方式),但它清楚地说明了正在发生的事情。如果你在几个月后回来(通常有人会这样做),那么你会花更少的时间来了解发生了什么。

最后的“on true”是一个经过深思熟虑的笛卡尔积,因为子查询“c”只能有一个结果,而您确实想要一个带有它的笛卡尔积。

子查询不一定有问题。

【讨论】:

这不能回答我的问题。您刚刚用大部分标准 sql 重写了 distinct on

以上是关于在不使用子查询的情况下使用 SELECT DISTINCT ON 计算总行数的主要内容,如果未能解决你的问题,请参考以下文章

在不使用相关子查询的情况下重写查询

如何在不使用多个子查询的情况下使用多列选择多行

有没有办法在不使用子查询的情况下根据不同的行计算平均值?

在不使用 Union ALL 的情况下添加多个 select 语句

如何在不使用 Join 的情况下处理引用其他表的相关子查询的问题

在 group by 中使用 datetime 日期并在单个 SELECT 中使用 order by 与使用子查询