从 GROUP BY 中选择单行且仅单行
Selecting single and only single rows from GROUP BY
我有两个table:parcel
和structure
,它们之间是一对多的关系:structure.parcel_id
指向parcel.id
.
我想要 select 所有 单个 结构。我当前的解决方案有效,但非常奇怪:
SELECT
max(column_1),
max(column_2),
max(column_3),
...
(twenty+ columns)
FROM structure
GROUP BY parcel_id
HAVING count(structure.id) = 1;
因为 structure.id
是不可空的并且上面的 HAVING
子句,根据定义,每个组只有一行。不幸的是 Postgres 没有意识到这一点,所以如果我说:
SELECT *
FROM structure
GROUP BY parcel_id
HAVING count(structure.id) = 1;
然后我得到关于需要对列使用聚合函数的预期错误。我用任意 max()
函数解决了这个问题,但这让试图理解代码的其他人感到困惑,它迫使我明确列出所有列,这意味着我必须重新开始并编辑这段代码每当添加一列时。 (不幸的是,这种情况在我的环境中经常发生。)
我有这个备用解决方案,它解决了我的大部分问题:
SELECT * FROM STRUCTURE
WHERE id IN (
SELECT
max(id) as id
FROM structure
GROUP by structure.parcel_id
HAVING count(structure.id) = 1
);
但是,由于查询的频率和 table 的大小,这显然给我的查询增加了不必要的缓慢,我想避免这种情况。
This question 与我要问的非常相似,但它会抓取每个组的第一行,而不是单个组的第一行(也是唯一一行)。
有什么优雅的方法可以解决这个问题吗?
每个请求的示例数据:
structure
table:
id | parcel_id | column_1 | column_2 | ...
------------------------------------------
1 | 536 | ... | .... | ...
2 | 536 | ... | .... | ...
3 | 537 | ... | .... | ...
4 | 538 | ... | .... | ...
5 | 538 | ... | .... | ...
6 | 539 | ... | .... | ...
7 | 540 | ... | .... | ...
8 | 541 | ... | .... | ...
9 | 541 | ... | .... | ...
想要的结果:
id | parcel_id | column_1 | column_2 | ...
------------------------------------------
3 | 537 | ... | .... | ...
6 | 539 | ... | .... | ...
7 | 540 | ... | .... | ...
请注意,537、539 和 540 是唯一不重复的 parcel_id
。
两个 table 都有约 150 万行和约 25 列。
使用window函数如何?
SELECT s.*
FROM (SELECT s.*, COUNT(*) OVER (PARTITION BY parcel_id) as cnt
FROM structure s
) s
WHERE cnt = 1;
但是,更有效的方法可能是:
select s.*
from structure s
where not exists (select 1
from structure s2
where s2.parcel_id = s.parcel_id and s2.id<> s.id
);
特别是,这可以利用 structure(parcel_id, id)
上的索引。
这应该快得多:
SELECT s.*
FROM (
SELECT parcel_id
FROM structure
GROUP BY 1
HAVING count(*) = 1
) s1
JOIN structure s USING (parcel_id);
您只需要 (parcel_id)
上的索引。
由于查询仅限于唯一的parcel_id
,所以子查询中不需要涉及id
。所以我们可以从 (parcel_id)
上的普通索引中得到一个 index-only scan - 并使用相同的索引进行连接。
连接应该比具有大子选择的 IN
快一点。 (尽管它们在现代 Postgres 中大多导致相同的查询计划。)
count(*)
比count(<expression>)
快一点,因为只需要确定行的存在。
旁白:
和 NOT EXISTS
反半连接应该也很快。您只需要 (parcel_id, id)
.
上的多列索引
question you linked to 用于 SQL 服务器。这是Postgres的一个更相关的相关问题:
- Select first row in each GROUP BY group?
我有两个table:parcel
和structure
,它们之间是一对多的关系:structure.parcel_id
指向parcel.id
.
我想要 select 所有 单个 结构。我当前的解决方案有效,但非常奇怪:
SELECT
max(column_1),
max(column_2),
max(column_3),
...
(twenty+ columns)
FROM structure
GROUP BY parcel_id
HAVING count(structure.id) = 1;
因为 structure.id
是不可空的并且上面的 HAVING
子句,根据定义,每个组只有一行。不幸的是 Postgres 没有意识到这一点,所以如果我说:
SELECT *
FROM structure
GROUP BY parcel_id
HAVING count(structure.id) = 1;
然后我得到关于需要对列使用聚合函数的预期错误。我用任意 max()
函数解决了这个问题,但这让试图理解代码的其他人感到困惑,它迫使我明确列出所有列,这意味着我必须重新开始并编辑这段代码每当添加一列时。 (不幸的是,这种情况在我的环境中经常发生。)
我有这个备用解决方案,它解决了我的大部分问题:
SELECT * FROM STRUCTURE
WHERE id IN (
SELECT
max(id) as id
FROM structure
GROUP by structure.parcel_id
HAVING count(structure.id) = 1
);
但是,由于查询的频率和 table 的大小,这显然给我的查询增加了不必要的缓慢,我想避免这种情况。
This question 与我要问的非常相似,但它会抓取每个组的第一行,而不是单个组的第一行(也是唯一一行)。
有什么优雅的方法可以解决这个问题吗?
每个请求的示例数据:
structure
table:
id | parcel_id | column_1 | column_2 | ...
------------------------------------------
1 | 536 | ... | .... | ...
2 | 536 | ... | .... | ...
3 | 537 | ... | .... | ...
4 | 538 | ... | .... | ...
5 | 538 | ... | .... | ...
6 | 539 | ... | .... | ...
7 | 540 | ... | .... | ...
8 | 541 | ... | .... | ...
9 | 541 | ... | .... | ...
想要的结果:
id | parcel_id | column_1 | column_2 | ...
------------------------------------------
3 | 537 | ... | .... | ...
6 | 539 | ... | .... | ...
7 | 540 | ... | .... | ...
请注意,537、539 和 540 是唯一不重复的 parcel_id
。
两个 table 都有约 150 万行和约 25 列。
使用window函数如何?
SELECT s.*
FROM (SELECT s.*, COUNT(*) OVER (PARTITION BY parcel_id) as cnt
FROM structure s
) s
WHERE cnt = 1;
但是,更有效的方法可能是:
select s.*
from structure s
where not exists (select 1
from structure s2
where s2.parcel_id = s.parcel_id and s2.id<> s.id
);
特别是,这可以利用 structure(parcel_id, id)
上的索引。
这应该快得多:
SELECT s.*
FROM (
SELECT parcel_id
FROM structure
GROUP BY 1
HAVING count(*) = 1
) s1
JOIN structure s USING (parcel_id);
您只需要 (parcel_id)
上的索引。
由于查询仅限于唯一的
parcel_id
,所以子查询中不需要涉及id
。所以我们可以从(parcel_id)
上的普通索引中得到一个 index-only scan - 并使用相同的索引进行连接。连接应该比具有大子选择的
IN
快一点。 (尽管它们在现代 Postgres 中大多导致相同的查询计划。)count(*)
比count(<expression>)
快一点,因为只需要确定行的存在。
旁白:
NOT EXISTS
反半连接应该也很快。您只需要 (parcel_id, id)
.
question you linked to 用于 SQL 服务器。这是Postgres的一个更相关的相关问题:
- Select first row in each GROUP BY group?