2016-04-13 5 views
0

У меня есть следующие данные:PostgreSQL выбрать максимум из строк

CREATE TABLE offer (
     id INTEGER, 
     product_id VARCHAR, 
     created_at TIMESTAMP, 
     amount INTEGER, 
     PRIMARY KEY (id)); 

INSERT INTO offer (id, product_id, created_at, amount) 
VALUES 
     (1, '123', '2016-03-12', 990), 
     (2, '136', '2016-02-01', 1056), 
     (3, '111', '2016-01-01', 1000), 
     (4, '123', '2016-01-02', 500); 

И я хотел бы получить строки с наибольшим количеством в product_id. Если взять эти предыдущие строки, я хотел бы получить идентификаторы: 2, 3 и 1, поскольку строка 1 содержит большее количество, чем строки 4.

id | product_id |  created_at  | amount 
----+------------+---------------------+-------- 
    2 | 136  | 2016-02-01 00:00:00 | 1056 
    3 | 111  | 2016-01-01 00:00:00 | 1000 
    1 | 123  | 2016-03-12 00:00:00 | 990 

Я пытался что-то подобное, но я не уверен в этом :

SELECT id, product_id, created_at, amount 
FROM offer 
ORDER BY 4, 2 DESC, 1, 3 

И еще я не могу попробовать.

ответ

1

Если я правильно понимаю, вы можете использовать distinct on:

select distinct on (product_id) o.* 
from offers o 
order by product_id, amount desc; 

distinct on является расширение Postgres. В этом случае он возвращает одну строку на product_id. Конкретный ряд - тот, который имеет наибольшую сумму, как определено amount desc.

+0

Я подозреваю, что вы имеете в виду 'ORDER BY', а не' GROUP BY'. –

+0

@JoachimIsaksson. , , Спасибо. –

1

Вы можете использовать RANK():

WITH cte AS 
(
    SELECT * , RANK() OVER (PARTITION BY product_id ORDER BY amount DESC) AS rnk 
    FROM Offers 
) 
SELECT id, product_id, created_at, amount 
FROM cte 
WHERE rnk = 1 
ORDER BY amount DESC; 

LiveDemo

Имейте в виду, что, если будет 2 или более product_id, созданные с разными датами, но тот же самый высокий amount все они будут вернулся.

Использование позиционного в ORDER BY не является лучшей практикой.

+1

«Имейте в виду, что если будет создано 2 или более product_id с разными датами, но с той же самой высокой суммой, они будут возвращены». Я не думал об этом случае, спасибо – Fr0z3n7

0

Попробуйте это:

SELECT o.* 
FROM offer o 
LEFT JOIN offer o1 ON o1.amount > o.amount AND o.product_id = o1.product_id 
WHERE o1.amount IS NULL 
1

Вы можете использовать PARTITION и RANK для создания рейтинга, используя сложные критерии:

SELECT 
    id, 
    product_id, 
    created_at, 
    amount, 
    RANK() OVER (
     PARTITION BY product_id 
     ORDER BY amount DESC 
     ) AS amount_rank_by_product_id 
FROM offer 
ORDER BY 
    product_id ASC, 
    amount_rank_by_product_id ASC 
; 
id | product_id |  created_at  | amount | amount_rank_by_product_id 
----+------------+---------------------+--------+--------------------------- 
    3 | 111  | 2016-01-01 00:00:00 | 1000 |       1 
    1 | 123  | 2016-03-12 00:00:00 | 990 |       1 
    4 | 123  | 2016-01-02 00:00:00 | 500 |       2 
    2 | 136  | 2016-02-01 00:00:00 | 1056 |       1 
(4 rows) 

Таким образом, вы можете использовать этот сгенерированный ранг для выбора ряды, которые вы хотите:

SELECT 
    o.id, 
    o.product_id, 
    o.created_at, 
    o.amount 
FROM 
    offer AS o 
    INNER JOIN (
     SELECT 
      id, 
      product_id, 
      RANK() OVER (
       PARTITION BY product_id 
       ORDER BY amount DESC 
       ) AS amount_rank 
     FROM offer 
     ) AS ar 
     USING (id) 
WHERE 
    ar.amount_rank = 1 
ORDER BY 
    o.amount DESC, 
    o.product_id ASC 
; 
id | product_id |  created_at  | amount 
----+------------+---------------------+-------- 
    2 | 136  | 2016-02-01 00:00:00 | 1056 
    3 | 111  | 2016-01-01 00:00:00 | 1000 
    1 | 123  | 2016-03-12 00:00:00 | 990 
(3 rows) 
Смежные вопросы