PostgreSQL从行中选择最大值

时间:2016-04-13 20:18:06

标签: sql postgresql greatest-n-per-group

我有以下数据:

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数量最多的行。 如果我采用这些先前的行,我想获得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

我现在还不能尝试。

4 个答案:

答案 0 :(得分: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 :(得分: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确定。

答案 2 :(得分:1)

您可以使用PARTITIONRANK使用复杂条件生成排名:

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)

答案 3 :(得分: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