SQL 按 parent 和 child 排序

SQL Order by parent and child

基本上我在这里的查询需要帮助。我想以正确的顺序排列,即 child 必须在 parents 名称下且按 A-Z 顺序排列。但是如果我在 child (Split 1) 下添加一个 subChild 似乎顺序是错误的。它应该在 Room Rose 之下。

p/s : 子子也可以创建另一个子子

HERE I PROVIDE A DEMO

感谢您帮助我正确订购此商品?

SELECT A.venueID
     , B.mainVenueID
     , A.venueName 
  FROM tblAdmVenue A 
  LEFT 
  JOIN tblAdmVenueLink B
    ON A.venueID = B.subVenueID
 ORDER   
    BY COALESCE(B.mainVenueID, A.venueID)
     , B.mainVenueID IS NOT NULL
     , A.venueID

我想要它 return 像这样的订单。

venueName
--------------
Banquet
Big Room
-Room Daisy
-Room Rose
  -Split 1
Hall
-Meeting Room WP

似乎这种递归方法也不起作用

WITH venue_ctg AS (
  SELECT A.venueID, A.venueName, B.mainVenueID 
  FROM tblAdmVenue A LEFT JOIN tblAdmVenueLink B
  ON A.venueID = B.subVenueID
  WHERE B.mainVenueID IS NULL 

  UNION ALL

  SELECT A.venueID, A.venueName, B.mainVenueID 
  FROM tblAdmVenue A LEFT JOIN tblAdmVenueLink B
  ON A.venueID = B.subVenueID
  WHERE B.mainVenueID IS NOT NULL
)
SELECT *
FROM venue_ctg ORDER BY venueName

给出的输出

只使用一个 table,而不是两个。第一个 table 包含所有需要的信息。

然后使用 WHERE mainVenueID IS NULL 行启动 CTE,不需要 JOIN

这可能是一个很好的教程:

它的'forest'接近你想要的。

对于您的数据,您可以使用: 要正确显示,您可以使用逗号之类的 SEPARATPR,拆分返回的数据,并检查层次结构

-- schema
CREATE TABLE tblAdmVenue (
    venueID VARCHAR(225) NOT NULL,
    venueName VARCHAR(225) NOT NULL,
    PRIMARY KEY(venueID)
);

CREATE TABLE tblAdmVenueLink (
    venueLinkID VARCHAR(225) NOT NULL,
    mainVenueID VARCHAR(225) NOT NULL,
    subVenueID VARCHAR(225) NOT NULL,
    PRIMARY KEY(venueLinkID)
    -- FOREIGN KEY (DepartmentId) REFERENCES Departments(Id)
);

-- data
INSERT INTO tblAdmVenue (venueID, venueName)
VALUES ('LA43', 'Big Room'), ('LA44', 'Hall'),
       ('LA45', 'Room Daisy'), ('LA46', 'Room Rose'),
       ('LA47', 'Banquet'), ('LA48', 'Split 1'),
       ('LA49', 'Meeting Room WP');

INSERT INTO tblAdmVenueLink (venueLinkID, mainVenueID, subVenueID)
VALUES ('1', 'LA43', 'LA45'), ('2', 'LA43', 'LA46'),
       ('3', 'LA46', 'LA48'), ('4', 'LA44', 'LA49');
✓

✓

✓

✓
with recursive cte (subVenueID, mainVenueID,level) as (
  select     subVenueID,
             mainVenueID, 1 as level
  from       tblAdmVenueLink
  union
  select     p.subVenueID,
             cte.mainVenueID,
             cte.level+1
  from       tblAdmVenueLink p
  inner join cte
          on p.mainVenueID = cte.subVenueID
)
select 
   
    CONCAT(GROUP_CONCAT(b.venueName  ORDER BY level DESC SEPARATOR  '-->') ,'-->',a.venueName)
from cte c 
LEFT JOIN tblAdmVenue a ON a.venueID = c.subVenueID 
LEFT JOIN tblAdmVenue b ON b.venueID = c.mainVenueID
GROUP BY subVenueID;
| CONCAT(GROUP_CONCAT(b.venueName  ORDER BY level DESC SEPARATOR  '-->') ,'-->',a.venueName) |
| :----------------------------------------------------------------------------------------- |
| Big Room-->Room Daisy                                                                      |
| Big Room-->Room Rose                                                                       |
| Big Room-->Room Rose-->Split 1                                                             |
| Hall-->Meeting Room WP                                                                     |

db<>fiddle here

我想你有:

  1. table tblAdmVenue A为会场列表;和
  2. table tblAdmVenueLink B 是树关系 table for parent-child

关于如何获得正确排序顺序的问题,我认为技巧之一是连接父场地名称。

with q0(venueID, venueName, mainVenueID, venuePath) as (
  select
    A.venueID,
    A.venueName,
    null,
    A.venueName
  from tblAdmVenue A
       left join tblAdmVenue B on A.venueID = B.subVenueID
  where B.mainVenueID is null
  union all
  select
    A.venueID,
    A.venueName,
    q0.venueID,
    q0.venuePath + char(9) + A.venueName
  from q0
       inner join tblAdmVenue B on q0.venueID = B.mainVenueID
       inner join tblAdmVenue A on A.venueID = B.subVenueID
)
select venueID, venueName, mainVenueID
from q0
order by venuePath

您希望数据按字母顺序和深度优先排序。

一个常见的解决方案是从顶部元素开始遍历结构,并连接到每个项目的路径。然后就可以直接使用路径下单了。

下面是如何在 MySQL 8.0 中使用递归查询

with recursive cte(venueID, venueName, mainVenueID, path, depth) as (
    select v.venueID, v.venueName, cast(null as char(100)), venueName, 0
    from tblAdmVenue v
    where not exists (select 1 from tblAdmVenueLink l where l.subVenueID = v.venueID)
    union all
    select v.venueID, v.venueName, c.venueID, concat(c.path, '/', v.venueName), c.depth + 1
    from cte c
    inner join tblAdmVenueLink l on l.mainVenueID = c.venueID
    inner join tblAdmVenue v on v.venueID = l.subVenueID
)
select * from cte order by path

递归查询的锚点选择顶部节点(即其 ID 不存在于 link table 的列 subVenueID 中的行)。然后,递归部分遵循关系。

作为奖励,我添加了一个 level 列来表示每个节点的深度,顶部节点从 0 开始。

Demo on DB Fiddle:

venueID | venueName       | mainVenueID | path                       | depth
:------ | :-------------- | :---------- | :------------------------- | ----:
LA47    | Banquet         | null        | Banquet                    |     0
LA43    | Big Room        | null        | Big Room                   |     0
LA45    | Room Daisy      | LA43        | Big Room/Room Daisy        |     1
LA46    | Room Rose       | LA43        | Big Room/Room Rose         |     1
LA48    | Split 1         | LA46        | Big Room/Room Rose/Split 1 |     2
LA44    | Hall            | null        | Hall                       |     0
LA49    | Meeting Room WP | LA44        | Hall/Meeting Room WP       |     1