我有几个表,在最简单的情况下它们是 1:N 相关的;例如:
CREATE TABLE X (id INTEGER, foo VARCHAR(16));
CREATE TABLE Y (id INTEGER, x_id INTEGER, bar VARCHAR(16));
里面有一些数据,例如:
INSERT INTO X VALUES (1, 'a'), (2, 'b'), (3, 'c');
INSERT INTO Y VALUES
(1, 1, 'blue'), (2, 1, 'green'), (3, 1, 'red'),
(4, 2, 'big'), (5, 2, 'small'),
(6, 3, 'car'), (7, 3, 'bike'), (8, 3, 'skate');
我想要做的:从由 x.id
限制的两个表中选择实体。例如,我需要选择2个实体,那么这将无法正常工作:
SELECT x.id, x.foo, y.id, y.bar
FROM x INNER JOIN y ON x.id = y.x_id
LIMIT 2
因为 if 将对最终结果行集应用限制,而不是基于 x.id
。结果应该是:
x.id x.foo y.id y.bar
1 'a' 1 'blue'
1 'a' 2 'green'
1 'a' 3 'red'
2 'b' 4 'big'
2 'b' 5 'small'
我的想法是编写一些 block 分子,它将“ block ”号添加到结果行集中,然后使用 WHERE
而不是 LIMIT
,所以:
SELECT block_number, x.id, x.foo, y.id, y.bar
FROM x INNER JOIN y ON x.id = y.x_id
WHERE block_number <= 2
但到目前为止我还没有找到任何好的方法来获取该区 block 号。 “好”的意思是:
- 没有子查询。原始选择可能相当复杂,涉及许多表,但速度很快,因为连接都是通过索引字段进行的。而且我不想破坏性能
- 理想情况下,没有新的连接。某些窗口函数可能是很好的解决方案,因为 Postgres 支持该功能
我尝试做这样的事情:
SELECT row_number() OVER (PARTITION BY x.id), x.id, x.foo, y.id, y.bar
FROM x INNER JOIN y ON x.id = y.x_id
WHERE block_number <= 2
但是,遗憾的是,它提供了in block 中的行号,而不是per block 的行号,因此不能用于我的问题。
此外,选择 x
条记录并在子查询中“预先”限制它,如下所示:
SELECT x.id, x.foo, y.id, y.bar
FROM
(SELECT x.id LIMIT 2) AS x
INNER JOIN y ON x.id = y.x_id
不是一个选项。这是因为:
- 原始查询包含所有表中的字段,但我不确定哪个字段属于哪个表。我可以添加一些机制来确定这一点,但这会减慢应用程序的速度。初始查询是动态生成的
- 更多,原始查询可能包含
ORDER BY
子句,这破坏了这个想法,因为我无法为仅适用于表的子查询编写正确的
ORDER BY
x
所以,重要补充:解决方案应该尊重原始查询顺序。最简单的说明方法是:
SELECT block_number, x.id, x.foo, y.id, y.bar
FROM x INNER JOIN y ON x.id = y.x_id
WHERE block_number <= 2
ORDER BY x.id DESC
应该产生:
block_number x.id x.foo y.id y.bar
1 3 'c' 6 'car'
1 3 'c' 7 'bike'
1 3 'c' 8 'skate'
2 2 'b' 4 'big'
2 2 'b' 5 'small'
即应正确计数 block 以保持原始顺序。而且,我无法预测初始顺序,因为它是动态指定的。
附注
抱歉,没有为您提供 sql fiddle。该资源对我不起作用,而且已经这样了很长一段时间了。
此外,我的 Postgres 版本是:9.4
,因此可以应用任何可能实现我的目标的"new"功能。
最佳答案
可以使用窗口函数dense_rank()
。
窗口函数不能在 WHERE 子句中调用,因此必须将其放在子查询中:
SELECT xid, foo, yid, bar
FROM (
SELECT
x.id xid, x.foo, y.id yid, y.bar,
dense_rank() over (order by x.id) rank
FROM x INNER JOIN y ON x.id = y.x_id
) sub
WHERE rank <= 2;
xid | foo | yid | bar
-----+-----+-----+-------
1 | a | 1 | blue
1 | a | 2 | green
1 | a | 3 | red
2 | b | 4 | big
2 | b | 5 | small
(5 rows)
关于sql - 按组限制结果集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35287437/