问题
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