找到最顶层的父母

时间:2014-12-11 11:01:08

标签: sql postgresql postgresql-9.3

用样本解释我的问题。

我有三张桌子ledger, balance, group

列是

  ledger ---> no, name, groupno
  balance --> ledgerno, balance
  group --> groupno, groupname, undergroupno

我想显示总账最多的分类账,其余为> 0

分类帐

no   name  groupno
1     A      5
2     B      4

平衡

ledgerno balance
 1          100
 2          200

groupno groupname undergroupno 
 1         AA        0
 2         BB        0
 3         CC        1
 4         DD        1
 5         EE        1
 6         FF        1
 7         GG        2
 8         HH        2
 9         II        2
 10        JJ        2

所以我想要这样的结果:

name     balance

 AA     
  CC
  DD
   B      100
   EE
    A     100
    FF

我尝试了以下with query,但没有显示正确的结果

WITH rel AS (
    SELECT groupname, amount
    FROM (
        WITH RECURSIVE rel_tree AS (
            SELECT groupno, groupname, undergroupno
            FROM "group"
            WHERE undergroupno = 0
          UNION ALL
            SELECT groupno, groupname, undergroupno
            FROM balance b
            INNER JOIN ledger l ON l.no = b.ledgerno
            INNER JOIN "group" g ON g.groupno = l.groupno AS tt
            INNER JOIN rel_tree r ON r.groupno = tt.undergroupno
        )
        SELECT *, 0 AS amount
        FROM rel_tree
        GROUP BY groupno, groupname, undergroupno
    )
SELECT *
FROM rel
UNION ALL
SELECT groupname, amount
FROM (
    SELECT name AS groupname, balance AS amount, groupname AS ord
    FROM balance b
    INNER JOIN ledger l ON l.no = b.ledgerno
    INNER JOIN "group" g ON g.groupno = l.groupno) AS ta
INNER JOIN rel ON rel.groupname = ta.ord

使用 postgresql 9.3

1 个答案:

答案 0 :(得分:1)

首先,永远不要使用SQL保留字作为表或列的名称。决不。 EVER。下面我使用 grp 而不是

其次,使用立即清楚的列名。下面我使用而不是 undergroupno

第三,这是一个非常好的问题,我很乐意花一些时间。我自己使用递归数据结构,正确的查询总是一个难题。

第四,你所说的你想要的是不可能的。您有一个表(grp)中的多行输出,其中散布着来自其他表的数据。我有一个非常接近您指定的解决方案。这是:

WITH tree AS (
    WITH RECURSIVE rel_tree(parent, groupno, refs, path) AS (
        SELECT groupno, groupno, 0, lpad(groupno::text, 4, '0') FROM grp WHERE parent > 0
    UNION
        SELECT g.parent, t.groupno, t.refs+1, lpad(g.parent::text, 4, '0') || '.' || t.path FROM grp g
        JOIN rel_tree t ON t.parent = g.groupno)
    SELECT * FROM rel_tree WHERE parent > 0 
    UNION
    SELECT groupno, groupno, 0 AS refs, lpad(groupno::text, 4, '0') FROM grp WHERE parent = 0)
SELECT repeat(' ', t.refs) || grp.groupname AS name, l.name AS ledger, b.balance
FROM grp
JOIN (
    SELECT DISTINCT ON (groupno) groupno, parent, max(refs) AS refs, path
    FROM tree
    GROUP BY parent, groupno, path
    ORDER BY groupno, path) t USING (groupno)
LEFT JOIN ledger l USING (groupno)
LEFT JOIN balance b ON b.ledgerno = l.no
ORDER BY t.path

这给出了输出:

name, ledger, balance
AA
 CC
 DD, B, 200
 EE, A, 100
 FF
BB
 GG
 HH
 II
 JJ

关于递归查询的几句话:

此查询产生一个自包含的完整层次结构。这意味着它为层次结构的每个节点列出了它的所有父节点,包括它自己。如果您将 CTE作为单独的查询运行,您会发现它返回的行数多于 grp 表中的10行。这是因为它列出了所有 grp 记录 groupno groupno ,但也为 parent ,另外所有父节点在层次结构中位于更高位置。在分析递归数据结构的其他属性(例如包含和父母身份)时,这种自包含的完整层次结构非常方便。

请注意,层次结构是自下而上构建的,从每个节点本身作为父节点开始,refs值为0(即 parent self 和一个只有 groupno 的路径作为文本值(用0&#39;填充)。递归在层次结构中向上运行,增加了ref值和更长的路径。在主查询中将完整列表修剪为每个 grp 记录的单个记录,可以通过路径进行排序。(请注意,使用 refs < / strong>可以省略并替换为长度(路径),但 refs 确实可以在其他使用类似查询的上下文中使用。)

此查询也适用于更高层次的层次结构。如果你添加:

11        KK        8
12        LL        8
13        MM        11

到表 grp ,查询将输出:

name, ledger, balance
AA
 CC
 DD, B, 200
 EE, A, 100
 FF
BB
 GG
 HH
  KK
   MM
  LL
 II
 JJ

请注意,路径适用于 groupno 值,最高可达9999.对于较大的 groupno 值,会增加前导0的值递归CTE,或考虑使用 ltree 扩展名以获得更大的灵活性。