Выберите первую строку в каждой группе GROUP BY? - PullRequest
1111 голосов
/ 27 сентября 2010

Как следует из названия, я бы хотел выбрать первую строку каждого набора строк, сгруппированных с GROUP BY.

В частности, если у меня есть таблица purchases, которая выглядит следующим образом:

SELECT * FROM purchases;

Мой вывод:

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;

Ожидаемый результат:

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

Ответы [ 14 ]

5 голосов
/ 29 декабря 2018

В SQL Server вы можете сделать это:

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

Объяснение: Здесь Группировка по выполняется на основе клиента, а затем заказывается по сумме, затем каждой такой группе даетсясерийный номер как StRank, и мы выбираем первого клиента, чей StRank равен 1

3 голосов
/ 04 января 2017

Принятое решение 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

Этот запрос выполняется достаточно быстро, особенно если в таблице покупок есть составной индекс, такой как (клиент, итог).

Примечание:

  1. t1, t2 - псевдоним подзапроса, который можно удалить в зависимости от базы данных.

  2. Предостережение : предложение using (...) в настоящее время не поддерживается в MS-SQL и Oracle db по состоянию на январь 2017 года. Вы должны расширить его, например, до. on t2.id = purchase.id и т. Д. Синтаксис USING работает в SQLite, MySQL и PostgreSQL.

1 голос
/ 18 января 2019

Для SQl Server наиболее эффективным способом является:

with
ids as ( --condition for split table into groups
    select i from (values (9),(12),(17),(18),(19),(20),(22),(21),(23),(10)) as v(i) 
) 
,src as ( 
    select * from yourTable where  <condition> --use this as filter for other conditions
)
,joined as (
    select tops.* from ids 
    cross apply --it`s like for each rows
    (
        select top(1) * 
        from src
        where CommodityId = ids.i 
    ) as tops
)
select * from joined

и не забудьте создать кластеризованный индекс для используемых столбцов

1 голос
/ 28 сентября 2018
  • Если вы хотите выбрать любую (в зависимости от ваших конкретных условий) строку из набора агрегированных строк.

  • Если вы хотите использовать другую (sum/avg) функцию агрегирования в дополнение к max/min.Таким образом, вы не можете использовать ключ с DISTINCT ON

Вы можете использовать следующий подзапрос:

SELECT  
    (  
       SELECT **id** FROM t2   
       WHERE id = ANY ( ARRAY_AGG( tf.id ) ) AND amount = MAX( tf.amount )   
    ) id,  
    name,   
    MAX(amount) ma,  
    SUM( ratio )  
FROM t2  tf  
GROUP BY name

Вы можете заменить amount = MAX( tf.amount ) любым условием, которое вы хотите, однимограничение: этот подзапрос не должен возвращать более одной строки

Но если вы хотите делать такие вещи, вы, вероятно, ищете оконные функции

...