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

Dav*_*ver 1205 sql sqlite postgresql group-by greatest-n-per-group

正如标题所示,我想选择用a组成的每组行的第一行GROUP BY.

具体来说,如果我有一个purchases看起来像这样的表:

SELECT * FROM purchases;
Run Code Online (Sandbox Code Playgroud)

我的输出:

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

我想查询每个产品id的最大购买量(total)customer.像这样的东西:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY total DESC;
Run Code Online (Sandbox Code Playgroud)

预期产出:

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

Erw*_*ter 1044

PostgreSQL中,这通常更简单,更快(下面的性能优化更多):

SELECT DISTINCT ON (customer)
       id, customer, total
FROM   purchases
ORDER  BY customer, total DESC, id;
Run Code Online (Sandbox Code Playgroud)

或者更短(如果不是很清楚)带有序数的输出列:

SELECT DISTINCT ON (2)
       id, customer, total
FROM   purchases
ORDER  BY 2, 3 DESC, 1;
Run Code Online (Sandbox Code Playgroud)

如果total可以为NULL(不会对任何方式造成伤害,但您希望匹配现有索引):

...
ORDER  BY customer, total DESC NULLS LAST, id;
Run Code Online (Sandbox Code Playgroud)

主要观点

  • DISTINCT ON是标准的PostgreSQL扩展(其中只定义DISTINCT了整个SELECT列表).

  • 列出DISTINCT ON子句中的任意数量的表达式,组合的行值定义重复项.手册:

    显然,如果两行在至少一个列值上不同,则认为它们是不同的.在此比较中,空值被认为是相等的.

    大胆强调我的.

  • DISTINCT ON可以结合使用ORDER BY.前导表达式必须以DISTINCT ON相同的顺序匹配前导表达式.您可以添加其他表达式以ORDER BY从每个对等组中选择特定行.我添加id了最后一项来打破关系:

    " id从每个组中选择最小的行,选择最高的行total."

    如果ORDER BY可以为NULL,则您很可能希望具有最大非空值的行.添加total如证明.细节:

  • NULLS LAST列表不受表达式SELECTDISTINCT ON以任何方式约束.(在上面的简单案例中不需要):

    • 不必ORDER BY或中包含任何表达式DISTINCT ON.

    • 可以ORDER BY列表中包含任何其他表达式.这有助于用子查询和聚合/窗口函数替换更复杂的查询.

  • 我使用Postgres版本8.3 - 11进行了测试.但是至少从版本7.1开始,该功能一直存在,所以基本上总是如此.

指数

上述查询的完美索引将是一个多列索引,它跨越匹配顺序中的所有三列并具有匹配的排序顺序:

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Run Code Online (Sandbox Code Playgroud)

可能太专业了.但是,如果特定查询的读取性能至关重要,请使用它.如果您SELECT在查询中,请在索引中使用相同的内容,以便排序顺序匹配并且索引适用.

有效性/性能优化

在为每个查询创建定制索引之前,权衡成本和收益.上述指数的潜力在很大程度上取决于数据分布.

使用索引是因为它提供了预先排序的数据.在Postgres 9.2或更高版本中,如果索引小于基础表,则查询也可以从仅索引扫描中受益.但是,索引必须完整扫描.

基准

我在这里有一个简单的基准,现在已经过时了.我在这个单独的答案中用详细的基准代替了它.

  • 对于大多数数据库大小来说,这是一个很好的答案,但我想指出,当你接近〜百万行时,"DISTINCT ON"变得非常慢.实现*总是*对整个表进行排序,并通过它扫描重复项,忽略所有索引(即使您已创建了所需的多列索引).有关可能的解决方案,请参见http://explainextended.com/2009/05/03/postgresql-optimizing-distinct/. (23认同)
  • 使用序数"使代码更短"是一个糟糕的主意.如何保留列名以使其可读? (14认同)
  • @KOTJMF:我建议你按照个人喜好去做.我展示了两种教育方法.语法速记对于`SELECT`列表中的长表达式非常有用. (11认同)
  • @PirateApp:不是从我的头上。DISTINCT ON仅适用于每组对等体获得*一行*。 (2认同)
  • 您可以使用“DISTINCT ON”而不使用“ORDER BY”。只是不要使用矛盾的“ORDER BY”。为此,您需要一个子查询。 (2认同)

OMG*_*ies 1019

在Oracle 9.2+(不是最初的8i +),SQL Server 2005 +,PostgreSQL 8.4 +,DB2,Firebird 3.0 +,Teradata,Sybase,Vertica:

WITH summary AS (
    SELECT p.id, 
           p.customer, 
           p.total, 
           ROW_NUMBER() OVER(PARTITION BY p.customer 
                                 ORDER BY p.total DESC) AS rk
      FROM PURCHASES p)
SELECT s.*
  FROM summary s
 WHERE s.rk = 1
Run Code Online (Sandbox Code Playgroud)

任何数据库支持:

但是你需要添加逻辑来打破关系:

  SELECT MIN(x.id),  -- change to MAX if you want the highest
         x.customer, 
         x.total
    FROM PURCHASES x
    JOIN (SELECT p.customer,
                 MAX(total) AS max_total
            FROM PURCHASES p
        GROUP BY p.customer) y ON y.customer = x.customer
                              AND y.max_total = x.total
GROUP BY x.customer, x.total
Run Code Online (Sandbox Code Playgroud)

  • `ROW_NUMBER()OVER(PARTITION BY [...])`以及其他一些优化帮助我查询从30秒到几毫秒.谢谢!(PostgreSQL 9.2) (28认同)
  • 如果有多个购买同时具有一个客户的最高`total`,则**1st**查询返回*任意*获胜者(取决于实现细节;`id`可以在每次执行时更改!).通常(并非总是)您需要每个客户*一行*,由其他标准定义,例如"具有最小`id`的那个".要修复,请将`id`附加到`row_number()`的`ORDER BY`列表中.然后你会得到与**2nd**查询相同的结果,这对于这种情况来说效率非常低*.此外,您还需要为每个其他列添加另一个子查询. (7认同)
  • Informix 12.x还支持窗口函数(虽然CTE需要转换为派生表).Firebird 3.0也将支持Window功能 (2认同)
  • Google的BigQuery还支持第一个查询的ROW_NUMBER()命令。对我们来说就像魅力 (2认同)
  • ROW_NUMBER() OVER(PARTITION 满足我的需求,但是有没有办法将组中的行号限制为 1 以减小视图的大小? (2认同)

Erw*_*ter 123

基准

测试中最有趣的候选人的Postgres 9.49.5用的中途现实表20万行purchases10K不同customer_id(平均每用户20行).

对于Postgres 9.5,我有效地为86446个不同的客户进行了第二次测试.见下文(每位客户平均2.3行).

建立

主表

CREATE TABLE purchases (
  id          serial
, customer_id int  -- REFERENCES customer
, total       int  -- could be amount of money in Cent
, some_column text -- to make the row bigger, more realistic
);
Run Code Online (Sandbox Code Playgroud)

我使用serial(下面添加的PK约束)和一个整数,customer_id因为这是一个更典型的设置.还添加了some_column以弥补通常更多的列.

虚拟数据,PK,索引 - 一个典型的表也有一些死元组:

INSERT INTO purchases (customer_id, total, some_column)    -- insert 200k rows
SELECT (random() * 10000)::int             AS customer_id  -- 10k customers
     , (random() * random() * 100000)::int AS total     
     , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM   generate_series(1,200000) g;

ALTER TABLE purchases ADD CONSTRAINT purchases_id_pkey PRIMARY KEY (id);

DELETE FROM purchases WHERE random() > 0.9; -- some dead rows

INSERT INTO purchases (customer_id, total, some_column)
SELECT (random() * 10000)::int             AS customer_id  -- 10k customers
     , (random() * random() * 100000)::int AS total     
     , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM   generate_series(1,20000) g;  -- add 20k to make it ~ 200k

CREATE INDEX purchases_3c_idx ON purchases (customer_id, total DESC, id);

VACUUM ANALYZE purchases;
Run Code Online (Sandbox Code Playgroud)

customer 表 - 用于高级查询

CREATE TABLE customer AS
SELECT customer_id, 'customer_' || customer_id AS customer
FROM   purchases
GROUP  BY 1
ORDER  BY 1;

ALTER TABLE customer ADD CONSTRAINT customer_customer_id_pkey PRIMARY KEY (customer_id);

VACUUM ANALYZE customer;
Run Code Online (Sandbox Code Playgroud)

在我的第二次 9.5 测试中,我使用了相同的设置,但random() * 100000生成customer_id只能获得几行customer_id.

表的对象大小 purchases

使用此查询生成.

               what                | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
 core_relation_size                | 20496384 | 20 MB        |           102
 visibility_map                    |        0 | 0 bytes      |             0
 free_space_map                    |    24576 | 24 kB        |             0
 table_size_incl_toast             | 20529152 | 20 MB        |           102
 indexes_size                      | 10977280 | 10 MB        |            54
 total_size_incl_toast_and_indexes | 31506432 | 30 MB        |           157
 live_rows_in_text_representation  | 13729802 | 13 MB        |            68
 ------------------------------    |          |              |
 row_count                         |   200045 |              |
 live_tuples                       |   200045 |              |
 dead_tuples                       |    19955 |              |
Run Code Online (Sandbox Code Playgroud)

查询

1. row_number()在CTE,(见其他答案)

WITH cte AS (
   SELECT id, customer_id, total
        , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
   FROM   purchases
   )
SELECT id, customer_id, total
FROM   cte
WHERE  rn = 1;
Run Code Online (Sandbox Code Playgroud)

2. row_number()在子查询中(我的优化)

SELECT id, customer_id, total
FROM   (
   SELECT id, customer_id, total
        , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
   FROM   purchases
   ) sub
WHERE  rn = 1;
Run Code Online (Sandbox Code Playgroud)

3. DISTINCT ON(见其他答案)

SELECT DISTINCT ON (customer_id)
       id, customer_id, total
FROM   purchases
ORDER  BY customer_id, total DESC, id;
Run Code Online (Sandbox Code Playgroud)

4.带子LATERAL查询的rCTE (见这里)

WITH RECURSIVE cte AS (
   (  -- parentheses required
   SELECT id, customer_id, total
   FROM   purchases
   ORDER  BY customer_id, total DESC
   LIMIT  1
   )
   UNION ALL
   SELECT u.*
   FROM   cte c
   ,      LATERAL (
      SELECT id, customer_id, total
      FROM   purchases
      WHERE  customer_id > c.customer_id  -- lateral reference
      ORDER  BY customer_id, total DESC
      LIMIT  1
      ) u
   )
SELECT id, customer_id, total
FROM   cte
ORDER  BY customer_id;
Run Code Online (Sandbox Code Playgroud)

5. customerLATERAL(见这里)

SELECT l.*
FROM   customer c
,      LATERAL (
   SELECT id, customer_id, total
   FROM   purchases
   WHERE  customer_id = c.customer_id  -- lateral reference
   ORDER  BY total DESC
   LIMIT  1
   ) l;
Run Code Online (Sandbox Code Playgroud)

6. array_agg()ORDER BY(见对方的回答)

SELECT (array_agg(id ORDER BY total DESC))[1] AS id
     , customer_id
     , max(total) AS total
FROM   purchases
GROUP  BY customer_id;
Run Code Online (Sandbox Code Playgroud)

结果

上述查询的执行时间EXPLAIN ANALYZE(以及所有选项关闭),最好是5次运行.

所有查询都使用了"仅索引扫描 " purchases2_3c_idx(以及其他步骤).其中一些只是针对较小的索引,其他更有效.

A. Postgres 9.4有200k行和~20 customer_id

1. 273.274 ms  
2. 194.572 ms  
3. 111.067 ms  
4.  92.922 ms  
5.  37.679 ms  -- winner
6. 189.495 ms
Run Code Online (Sandbox Code Playgroud)

B.与Postgres 9.5相同

1. 288.006 ms
2. 223.032 ms  
3. 107.074 ms  
4.  78.032 ms  
5.  33.944 ms  -- winner
6. 211.540 ms  
Run Code Online (Sandbox Code Playgroud)

C.与B.相同,但每行约2.3行 customer_id

1. 381.573 ms
2. 311.976 ms
3. 124.074 ms  -- winner
4. 710.631 ms
5. 311.976 ms
6. 421.679 ms
Run Code Online (Sandbox Code Playgroud)

2011年的原始(过时)基准

我在PostgreSQL 9.1上运行了三次测试,在一个包含65579行的实际生命表上,在所涉及的三列中的每一列上都有单列btree索引,并且执行了5次运行的最佳执行时间.
@OMGPonies的第一个查询(A)与上述DISTINCT ON解决方案(B)进行比较:

  1. 选择整个表,在这种情况下结果为5958行.

    A: 567.218 ms
    B: 386.673 ms
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用条件WHERE customer BETWEEN x AND y导致1000行.

    A: 249.136 ms
    B:  55.111 ms
    
    Run Code Online (Sandbox Code Playgroud)
  3. 选择一个客户WHERE customer = x.

    A:   0.143 ms
    B:   0.072 ms
    
    Run Code Online (Sandbox Code Playgroud)

用另一个答案中描述的索引重复相同的测试

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Run Code Online (Sandbox Code Playgroud)

1A: 277.953 ms  
1B: 193.547 ms

2A: 249.796 ms -- special index not used  
2B:  28.679 ms

3A:   0.120 ms  
3B:   0.048 ms
Run Code Online (Sandbox Code Playgroud)

  • 感谢一个伟大的基准.我想知道是否查询*events*数据,你有一个时间戳而不是*total*将受益于新的BRIN索引.这可能会为时间查询提供加速. (5认同)
  • @jangorecki:任何带有物理排序数据的*huge*表都可以从BRIN索引中获益. (3认同)

TMS*_*TMS 50

这是常见的问题,已经有经过充分测试和高度优化的解决方案.我个人更喜欢Bill Karwin左连接解决方​​案(原始帖子中有很多其他解决方案).

请注意,对于这个常见问题的一堆解决方案可以在大多数官方来源,MySQL手册中找到!请参阅常见查询示例::保持某个列的分组最大值的行.

  • 对于Postgres/SQLite(更不用说SQL)问题,MySQL手册如何"官方"?另外,要清楚的是,'DISTINCT ON`版本要短得多,更简单,并且在Postgres中的表现通常比使用自我"LEFT JOIN"或使用"NOT EXISTS"的半反连接的替代品更好.它也"经过了良好的测试". (22认同)
  • 很棒的参考.我不知道这被称为每组最大的问题.谢谢. (6认同)
  • 除了Erwin写的内容之外,我还说使用窗口函数(现在是常见的SQL功能)几乎总是比使用派生表的连接更快 (3认同)

Pau*_*rth 27

在Postgres中你可以array_agg像这样使用:

SELECT  customer,
        (array_agg(id ORDER BY total DESC))[1],
        max(total)
FROM purchases
GROUP BY customer
Run Code Online (Sandbox Code Playgroud)

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

有些事情需要注意:

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


use*_*394 12

由于存在SubQ,Erwin指出解决方案效率不高

select * from purchases p1 where total in
(select max(total) from purchases where p1.customer=customer) order by total desc;
Run Code Online (Sandbox Code Playgroud)


Ale*_*elo 8

非常快的解决方案

SELECT a.* 
FROM
    purchases a 
    JOIN ( 
        SELECT customer, min( id ) as id 
        FROM purchases 
        GROUP BY customer 
    ) b USING ( id );
Run Code Online (Sandbox Code Playgroud)

如果表由id索引,那么非常快

create index purchases_id on purchases (id);
Run Code Online (Sandbox Code Playgroud)


mat*_*tiu 8

我用这种方式(仅限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
);
Run Code Online (Sandbox Code Playgroud)

那么你的例子应该工作,几乎为是:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY FIRST(total) DESC;
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

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


编辑2 - 订购和过滤

如果您使用聚合函数(如这些),您可以订购结果,而无需已经订购数据:

http://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES
Run Code Online (Sandbox Code Playgroud)

所以等效的例子,排序将是这样的:

SELECT first(id order by id), customer, first(total order by id)
  FROM purchases
 GROUP BY customer
 ORDER BY first(total);
Run Code Online (Sandbox Code Playgroud)

当然,您可以按照您认为适合的方式订购和过滤; 它的语法非常强大.


kha*_*maa 7

查询:

SELECT purchases.*
FROM purchases
LEFT JOIN purchases as p 
ON 
  p.customer = purchases.customer 
  AND 
  purchases.total < p.total
WHERE p.total IS NULL
Run Code Online (Sandbox Code Playgroud)

这是如何运作的!(我去过那儿)

我们希望确保每次购买的总数最高.


一些理论上的东西(如果您只想了解查询,请跳过此部分)

设Total为函数T(customer,id),返回给定名称和id的值为了证明给定的总数(T(customer,id))是最高的,我们必须证明我们要证明

  • ∀xT(客户,身份证)> T(客户,x)(此总额高于该客户的所有其他总额)

要么

  • ¬∃xT(客户,ID)<T(客户,x)(该客户没有更高的总数)

第一种方法需要我们获取我不喜欢的那个名字的所有记录.

第二个需要一个聪明的方式来说没有比这个更高的记录.


回到SQL

如果我们在名称上加入表,并且总数少于连接表:

      LEFT JOIN purchases as p 
      ON 
      p.customer = purchases.customer 
      AND 
      purchases.total < p.total
Run Code Online (Sandbox Code Playgroud)

我们确保所有具有相同用户总数较高的记录的记录加入:

purchases.id, purchases.customer, purchases.total, p.id, p.customer, p.total
1           , Tom           , 200             , 2   , Tom   , 300
2           , Tom           , 300
3           , Bob           , 400             , 4   , Bob   , 500
4           , Bob           , 500
5           , Alice         , 600             , 6   , Alice   , 700
6           , Alice         , 700
Run Code Online (Sandbox Code Playgroud)

这将有助于我们过滤每次购买的最高总额,而无需分组:

WHERE p.total IS NULL

purchases.id, purchases.name, purchases.total, p.id, p.name, p.total
2           , Tom           , 300
4           , Bob           , 500
6           , Alice         , 700
Run Code Online (Sandbox Code Playgroud)

这就是我们需要的答案.


小智 7

first_value在 PostgreSQL 中,另一种可能性是结合使用窗口函数SELECT DISTINCT

select distinct customer_id,
                first_value(row(id, total)) over(partition by customer_id order by total desc, id)
from            purchases;
Run Code Online (Sandbox Code Playgroud)

我创建了一个 复合(id, total),因此两个值都由同一聚合返回。当然,您始终可以申请first_value()两次。


Diw*_*del 6

在SQL Server中,您可以执行以下操作:

SELECT *
FROM (
SELECT ROW_NUMBER()
OVER(PARTITION BY customer
ORDER BY total DESC) AS StRank, *
FROM Purchases) n
WHERE StRank = 1
Run Code Online (Sandbox Code Playgroud)

说明:这里的 分组依据是根据客户进行的,然后按总数进行订购,然后每个此类组的序列号都称为StRank,我们要取出第一个StRank为1的客户


Val*_*tin 6

ARRAY_AGGPostgreSQLU-SQLIBM DB2Google BigQuery SQL使用功能:

SELECT customer, (ARRAY_AGG(id ORDER BY total DESC))[1], MAX(total)
FROM purchases
GROUP BY customer
Run Code Online (Sandbox Code Playgroud)


unc*_*bob 6

这样它对我有用:

SELECT article, dealer, price
FROM   shop s1
WHERE  price=(SELECT MAX(s2.price)
              FROM shop s2
              WHERE s1.article = s2.article
              GROUP BY s2.article)
ORDER BY article;
Run Code Online (Sandbox Code Playgroud)

选择每件商品的最高价格


小智 6

这就是我们如何通过使用 windows 函数来实现这一点:

    create table purchases (id int4, customer varchar(10), total integer);
    insert into purchases values (1, 'Joe', 5);
    insert into purchases values (2, 'Sally', 3);
    insert into purchases values (3, 'Joe', 2);
    insert into purchases values (4, 'Sally', 1);
    
    select ID, CUSTOMER, TOTAL from (
    select ID, CUSTOMER, TOTAL,
    row_number () over (partition by CUSTOMER order by TOTAL desc) RN
    from purchases) A where RN = 1;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Joh*_*ong 5

从我的测试来看,公认的 OMG Ponies 的“受任何数据库支持”解决方案具有良好的速度。

在这里,我提供了一个相同的方法,但更完整和干净的任何数据库解决方案。考虑平局(假设希望为每个客户只获取一行,甚至为每个客户的最大总数获取多个记录),并且将为购买表中的真实匹配行选择其他购买字段(例如purchase_ payment_id)。

任何数据库都支持:

select * from purchase
join (
    select min(id) as id from purchase
    join (
        select customer, max(total) as total from purchase
        group by customer
    ) t1 using (customer, total)
    group by customer
) t2 using (id)
order by customer
Run Code Online (Sandbox Code Playgroud)

该查询相当快,特别是当购买表上存在诸如(客户,总计)之类的复合索引时。

评论:

  1. t1、t2 是子查询别名,可以根据数据库删除。

  2. 警告using (...)截至 2017 年 1 月的编辑,MS-SQL 和 Oracle 数据库目前不支持该子句。您必须自己将其扩展为例如等on t2.id = purchase.id。USING 语法适用于 SQLite、MySQL 和 PostgreSQL。


Luk*_*zda 5

Snowflake/Teradata 支持QUALIFY类似于HAVING窗口函数的子句:

SELECT id, customer, total
FROM PURCHASES
QUALIFY ROW_NUMBER() OVER(PARTITION BY p.customer ORDER BY p.total DESC) = 1
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

945566 次

最近记录:

5 年,9 月 前