在每个GROUP BY组中选择第一行?

﹥>﹥吖頭↗ 提交于 2019-12-10 21:37:07

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

顾名思义,我想选择以GROUP BY分组的每组行的第一行。

具体来说,如果我有一个如下的purchases表:

SELECT * FROM purchases;

我的输出:

id | customer | total
---+----------+------
 1 | Joe      | 5
 2 | Sally    | 3
 3 | Joe      | 2
 4 | Sally    | 1

我想查询每个customer购买的最大商品的idtotal )。 像这样:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY total DESC;

预期产量:

FIRST(id) | customer | FIRST(total)
----------+----------+-------------
        1 | Joe      | 5
        2 | Sally    | 3

#1楼

由于存在SubQ,该解决方案不是十分有效,正如Erwin指出的那样

select * from purchases p1 where total in
(select max(total) from purchases where p1.customer=customer) order by total desc;

#2楼

这是常见的每组最多n个问题,该问题已经过测试和高度优化 。 就我个人而言,我更喜欢Bill Karwin左联接解决方案带有许多其他解决方案原始帖子 )。

注意,在大多数官方资料之一MySQL手册中 ,可以惊奇地找到许多针对这个常见问题的解决方案! 请参阅常见查询的示例::持有特定列的按组最大值的行


#3楼

快速解决方案

SELECT a.* 
FROM
    purchases a 
    JOIN ( 
        SELECT customer, min( id ) as id 
        FROM purchases 
        GROUP BY customer 
    ) b USING ( id );

如果用ID索引表,这真的非常快:

create index purchases_id on purchases (id);

#4楼

在Postgres中,您可以像这样使用array_agg

SELECT  customer,
        (array_agg(id ORDER BY total DESC))[1],
        max(total)
FROM purchases
GROUP BY customer

这将为您提供每个客户最大购买量的id

注意事项:

  • array_agg是一个聚合函数,因此可以与GROUP BY
  • array_agg允许您指定array_agg于自身的排序,因此它不会限制整个查询的结构。 如果需要执行一些与默认值不同的操作,则还提供了有关如何对NULL进行排序的语法。
  • 构建数组后,我们将获取第一个元素。 (Postgres数组是1索引的,而不是0索引的)。
  • 您可以以类似的方式将array_agg用于第三输出列,但max(total)更简单。
  • DISTINCT ON不同,使用array_agg可使您保留GROUP BY ,以防其他原因。

#5楼

我使用这种方式(仅适用于postgresql): https : //wiki.postgresql.org/wiki/First/last_%28aggregate%29

-- Create a function that always returns the first non-NULL item
CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $1;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.first (
        sfunc    = public.first_agg,
        basetype = anyelement,
        stype    = anyelement
);

-- Create a function that always returns the last non-NULL item
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $2;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.last (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

然后您的示例应该几乎可以按以下方式工作:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY FIRST(total) DESC;

CAVEAT:忽略NULL行


编辑1-改用postgres扩展名

现在,我使用这种方式: http : //pgxn.org/dist/first_last_agg/

要在ubuntu 14.04上安装:

apt-get install postgresql-server-dev-9.3 git build-essential -y
git clone git://github.com/wulczer/first_last_agg.git
cd first_last_app
make && sudo make install
psql -c 'create extension first_last_agg'

这是一个postgres扩展,为您提供第一个和最后一个功能; 显然比上述方法快。


编辑2-排序和过滤

如果使用聚合函数(如此类),则可以对结果进行排序,而无需对数据进行排序:

http://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES

因此,带有排序的等效示例如下所示:

SELECT first(id order by id), customer, first(total order by id)
  FROM purchases
 GROUP BY customer
 ORDER BY first(total);

当然,您可以按自己认为合适的顺序进行排序和过滤。 这是非常强大的语法。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!