【问题标题】:How to Order table records with parent followed by child using SQL Query如何使用 SQL Query 对表记录进行排序,父项后跟子项
【发布时间】:2019-08-02 09:31:41
【问题描述】:

我有一个表,其中包含几个具有父记录的记录,如下所示。并非所有人都有父记录。

id | parent_id
--------------
1 |  0
2 |  0
3 |  1
4 |  0
5 |  0
6 |  0
7 |  5

我希望记录的顺序是先父后子:

id | parent_id
--------------
1 |  0
3 |  1
2 |  0
4 |  0
5 |  0
7 |  5
6 |  0

如何在不使用存储过程的情况下使用 SQL 查询来实现这一点?

我正在使用 postgres。

【问题讨论】:

  • 不相关,但是:存储 0 而不是 null 来表示“没有父级”是一个坏主意。这会阻止您使用正确的外键约束
  • 您的示例恰好暗示了 1 级层次结构。是这样吗?并正确执行?

标签: sql postgresql hierarchical-data


【解决方案1】:

你需要一个recursive query,它通过所有级别携带根ID,然后你可以按它对行进行排序:

with recursive entries as (
  select id, parent_id, id as root_id, 1 as level
  from the_table
  where parent_id = 0 -- this should be IS NULL
  union all 
  select c.id, c.parent_id, p.root_id, p.level + 1
  from the_table c
    join entries p on p.id = c.parent_id
)
select id, parent_id
from entries
order by root_id, level, id;

在线示例:https://rextester.com/YKUJ56922

【讨论】:

  • 这并没有给我想要的结果(我在原始问题中发布的那个)
  • @m.beginner:在线示例完全返回您在问题中列出的结果。
  • 这会首先生成所有父 ID 为 0 的记录,然后打印具有正确父 ID 和子链接的记录。我不想首先转储所有父 ID 为 0 的记录
  • 再次:这将返回完全您在问题中显示为所需输出的内容。如果这不是您想要的,您需要更新您的问题以显示您真正想要的。
  • 这只是似乎实现了“父后跟子”,因为OP的测试用例构造不佳,总是parent.id < child.idparent.id > child.id 失败。
【解决方案2】:

假设正如您的示例所暗示的那样,可能只有一层层次结构:

SELECT child.*
FROM   tbl      AS child
LEFT   JOIN tbl AS parent ON parent.id = child.parent_id
ORDER  BY COALESCE(parent.id, child.id)  -- order by parent if exists
        , parent.id IS NOT NULL          -- parent first per group
        , child.id;                      -- order rest by id

仅当我们按一些附加属性(如“名称”)排序时才需要连接(这是典型的情况,因为代理 ID 的值没有意义)。虽然仅像您演示的那样按 ID 排序,但我们不需要连接,因为所有信息都已经存在(就像 Gordon 也演示的那样)。然后我们可以简化:

SELECT *
FROM   tbl
ORDER  BY CASE WHEN parent_id = 0 THEN id ELSE parent_id END
        , parent_id <> 0
        , id;

dbfiddle here - 使用扩展测试用例来证明 ORDER BY 项目的相关性。

【讨论】:

  • @m.beginner:我添加了更多解释。
  • @m.beginner:如果您觉得这对您来说是更好的答案:稍后更改已接受的答案没有任何问题。
【解决方案3】:

我想你想要:

order by coalesce(nullif(parent_id, 0), id), id

基本上,忽略parent_id 中的零。然后使用parent_id(如果存在),否则使用id

Here 是一个 dbfiddle。

此版本假定父 ID 小于子 ID - 这在您的数据中是正确的,并且在大多数情况下是有意义的。如果您想明确排序:

order by coalesce(nullif(parent_id, 0), id),
         (parent_id = 0) desc,
         id

【讨论】:

  • 这非常有用而且非常简单。但是,这也会产生与下面建议的 RECURSIVE 查询相同的结果。这会首先转储所有父 ID 为 0 的记录,然后生成具有正确父子顺序的记录列表。但是我不想首先列出所有 source_id 为 0 的记录
  • @m.beginner 。 . .它肯定没有。它完全符合您的问题。我添加了一个 dbfiddle 来演示这一点。
  • 聪明,但仍然无法将父母排在孩子之前。
  • @ErwinBrandstetter 。 . .它适用于提供的数据,我认为在孩子之前定义父母通常是有意义的。但是,如果示例数据不相关,我编辑了答案。
  • @Gordon:是的,在孩子之前创建父母似乎是有道理的。但是在没有 FK 的情况下不必如此(即使是可延期的也不必如此)。此外,ID 仍然不必因为首先添加了行而变得更小。甚至没有serial 列。
【解决方案4】:

我用这种方式(Postgres):

SELECT id, parent_id, name 
FROM   my_table 
ORDER BY 
COALESCE(parent_id,id)||id::varchar

【讨论】:

    猜你喜欢
    • 2018-10-13
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多