PostgreSQL select max from rows

和自甴很熟 提交于 2019-12-12 02:54:24

问题


I have the following data:

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);

And I would like to get rows with the highest amount per product_id. If I take these previous rows I would like to get IDs: 2, 3 and 1 because row 1 contains a greater amount than row 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

I tried something like that but I'm not sure about it:

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

And I can't try it live yet.


回答1:


If I understand correctly, you can use distinct on:

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

distinct on is a Postgres extension. In this case, it returns one row per product_id. The particular row is the one with the largest amount, as determined by amount desc.




回答2:


You could use 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

Keep in mind that if there will be 2 or more product_id created with different dates but the same highest amount they will all be returned.

Using positional in ORDER BY is not best practice.




回答3:


You can use PARTITION and RANK to generate a ranking using complex criteria:

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)

So you can then use that generated rank to select the rows you want:

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)



回答4:


Try this one:

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


来源:https://stackoverflow.com/questions/36608771/postgresql-select-max-from-rows

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