如何从具有计数的父表中选择第n个子元素

时间:2014-10-22 19:03:19

标签: sql postgresql parent-child

假设我有两个表:

create table parents (id integer, name varchar, child_count integer);
create table children (id integer, name varchar, parent_id integer);

假设父母:孩子的比例约为1:1000。

假设child_count定期更新(或通过PostgreSQL中的物化视图提供),例如使用此更新语句:

update parents p set child_count = pc.count
from (select p.id id, count(c.*) count
    from parents p join children c on p.id = c.parent_id
    group by p.id) as pc
where p.id = pc.id;

我希望在所有孩子中找到第n个孩子,其中孩子首先按父母姓名排序,然后按孩子名称排序。基本上,我想要这个查询:

select c.*
from children c join parents p on p.id = c.parent_id
order by p.name, c.name
limit 42 offset 42;

...除了这是一个昂贵的查询需要扫描孩子,但我知道使用child_count字段可以更便宜地完成 - 冒着可能过时的风险,但我并不担心这一点。

有没有办法编写一个查询(最好是postgresql),使用父亲的child_count来选择所有父母的第n个孩子?

或者有更好的方法来构建数据吗?我会考虑使用范围(无论是postgresql本机还是使用两个单独的列),除非它们似乎很难更新。我可能想要使用不同的排序顺序选择第n个孩子(但总是使用父母,然后是孩子进行排序)。

我目前最好的想法是将所有父项缓存在内存中,并在我自己发现正确的父项后使用限制/偏移查询,但也可能有相当多的父母。

1 个答案:

答案 0 :(得分:2)

似乎row_number window function(以及thisthis也可能正是您所需要的):

SELECT x.*
FROM (  SELECT  p.id AS parent_id,
                p.name AS parent_name,
                c.id AS child_id,
                c.name AS child_name,
                row_number() OVER ( ORDER BY p.name, c.name ) AS ordinal
        FROM    children c
                JOIN parents p ON c.parent_id = p.id
    ) AS x
WHERE x.ordinal = 42
ORDER BY p.name, c.name;

事实上,通过上述查询,您甚至不需要保留child_count列(您可能因其他原因而想要这样做)。