如何获取不在 GROUP BY 中的列?
How to get columns which are not in GROUP BY?
我有一个 Postgresql 数据库,其中有这两个表。
shipping_method
id | name | abbrev
---+----------+-------
1 | Standard | ST
2 | Express | EX
shipping_details
:
id
shipping_method_id
estimated_time_min
estimated_time_max
price
2
1
02:00:00
04:00:00
230
3
2
00:03:00
01:00:00
500
4
1
02:00:00
04:00:00
1230
5
1
02:00:00
04:00:00
850
6
2
01:00:00
02:00:00
1785
我的目标是获取每种运输方式中最昂贵的运输详细信息(对于特定产品 [不在 OP 中])。
到目前为止,我写了这个查询:
SELECT
sm.id, sm.name, MAX(sd.price) AS max_price
FROM
shipping_details AS sd
LEFT JOIN
shipping_method AS sm ON sm.id = sd.shipping_method_id
GROUP BY
sm.id
哪个returns:
id | name | max_price
---+----------+---------
2 | Express | 1785
1 | Standard | 1230
通过该查询,如果不将它们放在 GROUP BY
子句中,我无法获得 shipping_details
列。我主要需要价格较高的每种特定运输方式的运输详细信息。
我怎样才能做到这一点?
使用DISTINCT ON
:
SELECT DISTINCT ON (sm.id) sm.id, sm.name, sd.price AS max_price
FROM shipping_details AS sd
LEFT JOIN shipping_method AS sm
ON sm.id = sd.shipping_method_id
ORDER BY sm.id, sd.price DESC;
以上逻辑将return具有最高价格的送货方式。
这是使用 window 函数的一种方法:
select *
from shipping_method sm
join (
select *, row_number() over (partition by shipping_method_id order by price desc) rn
from shipping_details sd) t
on sd.shipping_method_id = t.id
and rn = 1 ;
要从 shipping_details
中价格最高的每一行中获取更多列,请使用 DISTINCT ON
:
SELECT sm.id, sm.name, sd.*
FROM shipping_method sm
LEFT JOIN (
SELECT DISTINCT ON (shipping_method_id)
shipping_method_id AS id, price AS max_price
-- add more columns as you like
FROM shipping_details sd
ORDER BY sd.shipping_method_id DESC, sd.price DESC, sd.id -- ①
) sd USING (id);
只要涉及 shipping_details
中的所有行,通常先聚合最快,然后 然后 加入。 (当 table 包含许多被连接消除的附加行时则不然。)
如果 price
可以 NULL
则 ORDER BY ... price DESC NULLS LAST
- 否则 NULL
按降序排列在顶部。请务必匹配现有索引。
① 如果 table shipping_details
很大,shipping_details (shipping_method_id, prize)
上的索引会使其变快。或者 (shipping_method_id DESC, prize DESC)
上的索引。两列的 排序顺序与查询 同步很重要。 Postgres 可以向前或向后扫描索引,但对于多列索引,所有列的排序顺序需要与查询同步。参见:
还有一个问题需要注意:如果可以有多个具有最高价格的运输详细信息,您会得到一个任意的选择,它可以随着每次执行而改变,通常是在写入相关行之后。要获得 stable、确定性结果,请添加更多 ORDER BY
表达式作为决胜局。就像我在上面附加的 sd.id
一样。那么最小的 id
就是赢家,一直如此。
如果有 many 这样的联系,甚至可以将 id
添加到索引中。喜欢 (shipping_method_id, prize, id DESC)
- 注意 id
!
的相反排序顺序
相关:
- Select first row in each GROUP BY group?
- Query with LEFT JOIN not returning rows for count of 0
可能有(更多)更快的技术,具体取决于未公开的细节。假设每个运送方式 many 行,以及所讨论的适用索引,这应该是 much 更快:
SELECT sm.id, sm.name, sd.*
FROM shipping_method sm
LEFT JOIN LATERAL (
SELECT sd.price AS max_price, id AS shipping_details_id
FROM shipping_details sd
WHERE sd.shipping_method_id = sm.id
ORDER BY sd.price DESC NULLS LAST
LIMIT 1
) sd ON true;
- Optimize GROUP BY query to retrieve latest row per user
- Select first row in each GROUP BY group?(注意第 5 条查询的性能!)
我有一个 Postgresql 数据库,其中有这两个表。
shipping_method
id | name | abbrev
---+----------+-------
1 | Standard | ST
2 | Express | EX
shipping_details
:
id | shipping_method_id | estimated_time_min | estimated_time_max | price |
---|---|---|---|---|
2 | 1 | 02:00:00 | 04:00:00 | 230 |
3 | 2 | 00:03:00 | 01:00:00 | 500 |
4 | 1 | 02:00:00 | 04:00:00 | 1230 |
5 | 1 | 02:00:00 | 04:00:00 | 850 |
6 | 2 | 01:00:00 | 02:00:00 | 1785 |
我的目标是获取每种运输方式中最昂贵的运输详细信息(对于特定产品 [不在 OP 中])。
到目前为止,我写了这个查询:
SELECT
sm.id, sm.name, MAX(sd.price) AS max_price
FROM
shipping_details AS sd
LEFT JOIN
shipping_method AS sm ON sm.id = sd.shipping_method_id
GROUP BY
sm.id
哪个returns:
id | name | max_price
---+----------+---------
2 | Express | 1785
1 | Standard | 1230
通过该查询,如果不将它们放在 GROUP BY
子句中,我无法获得 shipping_details
列。我主要需要价格较高的每种特定运输方式的运输详细信息。
我怎样才能做到这一点?
使用DISTINCT ON
:
SELECT DISTINCT ON (sm.id) sm.id, sm.name, sd.price AS max_price
FROM shipping_details AS sd
LEFT JOIN shipping_method AS sm
ON sm.id = sd.shipping_method_id
ORDER BY sm.id, sd.price DESC;
以上逻辑将return具有最高价格的送货方式。
这是使用 window 函数的一种方法:
select *
from shipping_method sm
join (
select *, row_number() over (partition by shipping_method_id order by price desc) rn
from shipping_details sd) t
on sd.shipping_method_id = t.id
and rn = 1 ;
要从 shipping_details
中价格最高的每一行中获取更多列,请使用 DISTINCT ON
:
SELECT sm.id, sm.name, sd.*
FROM shipping_method sm
LEFT JOIN (
SELECT DISTINCT ON (shipping_method_id)
shipping_method_id AS id, price AS max_price
-- add more columns as you like
FROM shipping_details sd
ORDER BY sd.shipping_method_id DESC, sd.price DESC, sd.id -- ①
) sd USING (id);
只要涉及 shipping_details
中的所有行,通常先聚合最快,然后 然后 加入。 (当 table 包含许多被连接消除的附加行时则不然。)
如果 price
可以 NULL
则 ORDER BY ... price DESC NULLS LAST
- 否则 NULL
按降序排列在顶部。请务必匹配现有索引。
① 如果 table shipping_details
很大,shipping_details (shipping_method_id, prize)
上的索引会使其变快。或者 (shipping_method_id DESC, prize DESC)
上的索引。两列的 排序顺序与查询 同步很重要。 Postgres 可以向前或向后扫描索引,但对于多列索引,所有列的排序顺序需要与查询同步。参见:
还有一个问题需要注意:如果可以有多个具有最高价格的运输详细信息,您会得到一个任意的选择,它可以随着每次执行而改变,通常是在写入相关行之后。要获得 stable、确定性结果,请添加更多 ORDER BY
表达式作为决胜局。就像我在上面附加的 sd.id
一样。那么最小的 id
就是赢家,一直如此。
如果有 many 这样的联系,甚至可以将 id
添加到索引中。喜欢 (shipping_method_id, prize, id DESC)
- 注意 id
!
相关:
- Select first row in each GROUP BY group?
- Query with LEFT JOIN not returning rows for count of 0
可能有(更多)更快的技术,具体取决于未公开的细节。假设每个运送方式 many 行,以及所讨论的适用索引,这应该是 much 更快:
SELECT sm.id, sm.name, sd.*
FROM shipping_method sm
LEFT JOIN LATERAL (
SELECT sd.price AS max_price, id AS shipping_details_id
FROM shipping_details sd
WHERE sd.shipping_method_id = sm.id
ORDER BY sd.price DESC NULLS LAST
LIMIT 1
) sd ON true;
- Optimize GROUP BY query to retrieve latest row per user
- Select first row in each GROUP BY group?(注意第 5 条查询的性能!)