sqlite 基于条件的多表递归查找

nhhxz33t  于 2023-05-29  发布在  SQLite
关注(0)|答案(2)|浏览(212)

我有多个表,我需要从中提取父/子信息的每一个表中的一个项目。例如:
表1:
| id|姓名|ParentIDFromTable2|重量|高度|说明|LocationIDFromTable3|
| - -----|- -----|- -----|- -----|- -----|- -----|- -----|
| 一个|杰克|5个|一百八十|一百八十三|T1描述1|一个|
| 2|麻雀|3|二百一十|一百六十九|T1描述2|一个|
| 3|约翰|六|三百五十|二百一十|T1描述3|一个|
| 4|吉尔|一个|一百一十|一百四十|T1描述4| 2|
| 5个|朱丽叶|七个|一百二十二|一百五十|T1描述5|一个|
表2:
| id|姓名|祖父母ID| IDFromTable3|说明|
| - -----|- -----|- -----|- -----|- -----|
| 一个|亚当|0|一个|Des1|
| 2|>扎克|一个|一个|Des2|
| 3|诺亚|2|一个|Des3|
| 4|雅各布|3|一个|Des4|
| 5个|>杰夫|4|一个|Des5|
| 六|德雷克|5个|一个|Des6|
| 七个|坎耶|3|一个|Des7|
表3:
| id|姓名|说明|
| - -----|- -----|- -----|
| 一个|P1| D1|
| 2| P2| D2|
| 3| P3| D3|
因此,对于表1中的每个项目,如果表2中的Name字段包含>字符,则应返回完整的父级层次结构沿着表2中的描述,并且仅使用第一个Name字段中的描述。例如:
| ID|姓名|重量|高度|说明|血统|
| - -----|- -----|- -----|- -----|- -----|- -----|
| 一个|杰克|一百八十|一百八十三|Des5| P1/Adam/Zack/Noah/Jacob/Jeff/Jack|
| 4|吉尔|一百一十|一百四十|T1描述4| P2/Adam/Jill|
| 5个|朱丽叶|一百二十二|一百五十|Des2| P1/亚当/扎克/诺亚/坎耶/朱丽叶|
我问了一个类似的问题,并认为我将能够找出条件描述,但没有运气到目前为止。
我将非常感谢你的帮助!

**[更新]**澄清:

1.在表2中,Location列保存表3的ID-因此为了清楚起见,我将表2中的Location列更新为IDFromTable 3
1.我面临的挑战是在进行递归时引入检查,以便如果在Table 2Name列中存在'>'字符,则表1的描述中的项目应更新为表2第一次出现的描述(of > character -如果例如在Table 2上的递归期间遇到多于1个在Name列中具有'>'字符的GrandParentID项)否则,它将使用其自身(表1)行中的描述
1.我已经修复了Table 1,以反映更新后的列LocationIDFromTable 3--换句话说,我们可以直接使用Table 1中的Table 3 ID,而不是转到Table 2

zfycwa2u

zfycwa2u1#

下面是一种使用递归的方法:

  • “* 基准步骤 *":从tab 1收集信息
  • “* 递归步骤 *":在匹配cte.ParentIDFromTable2 = tab2.id时将步骤n-1的cte与tab 2连接。通过在开头连接相同的字符,使>前缀名称的描述可识别。

递归完成后,您可以:

  • FIRST_VALUE收集第一个相关描述,并将记录排序到...
  • 使用GROUP_CONCAT对姓名进行聚合,以获取您的血统
  • 最后与tab 3连接以收集祖先信息

每一个都在子查询中执行。

WITH RECURSIVE cte AS (
    SELECT id AS firstId, name AS firstName, 
           NULL AS lastId, name, ParentIDFromTable2, Weight, Height,
           Description, LocationIDFromTable3 AS Location
    FROM tab1

    UNION ALL
  
    SELECT cte.firstId, cte.firstName, 
           tab2.id, TRIM(tab2.Name, '>'), tab2.GrandParentID, NULL, NULL, 
           CASE WHEN SUBSTR(tab2.Name, 0, 2)='>' 
                THEN '>' || tab2.Description
           END AS Description, NULL
    FROM       cte 
    INNER JOIN tab2
            ON cte.ParentIDFromTable2 = tab2.id
), cte2 AS (
    SELECT firstId, firstName, Weight, Height, name, lastId,
           FIRST_VALUE(Description) OVER(
               PARTITION BY firstId 
               ORDER     BY Description IS NULL,
                            SUBSTR(name, 0, 2)='>' DESC, 
                            lastid DESC
           ) AS Description, 
           Location
    FROM cte 
    ORDER BY firstId, ParentIDFromTable2
), cte3 AS (
    SELECT firstId                 AS id, 
           firstName               AS name,
           MAX(Weight)             AS weight,
           MAX(Height)             AS height,
           MIN(Description)        AS description,
           MAX(Location)           AS location,
           GROUP_CONCAT(name, '/') AS lineage
    FROM cte2 
    GROUP BY firstId, 
             firstName
)
SELECT cte3.id, 
       cte3.name, 
       cte3.weight, 
       cte3.height, 
       TRIM(cte3.description, '>') AS description, 
       tab3.name || '/' || cte3.lineage AS lineage
FROM       cte3
INNER JOIN tab3
        ON tab3.id = cte3.location

输出

| id|姓名|重量|高度|描述|世系|
| - -----|- -----|- -----|- -----|- -----|- -----|
| 一个|杰克|一百八十|一百八十三|Des5| P1/Adam/Zack/Noah/Jacob/Jeff/Jack|
| 2|麻雀|二百一十|一百六十九|Des2| P1/亚当/扎克/诺亚/麻雀|
| 3|约翰|三百五十|二百一十|Des5| P1/Adam/Zack/Noah/Jacob/Jeff/Drake/John|
| 4|吉尔|一百一十|一百四十|T1描述4| P1/Adam/Jill|
| 5个|朱丽叶|一百二十二|一百五十|Des2| P1/亚当/扎克/诺亚/坎耶/朱丽叶|
查看演示here

EDIT:尝试删除一些要在末尾添加的列如下:

  • 在递归CTE中保留所有需要更新的列,
  • 添加上次连接操作以恢复丢失的字段

在下面的示例中,我们将删除字段“weight”和“height”,以便在最后一步中添加它们。

WITH RECURSIVE cte AS (
    SELECT id AS firstId, name AS firstName, 
           NULL AS lastId, name, ParentIDFromTable2,
           Description, LocationIDFromTable3 AS Location
    FROM tab1

    UNION ALL
  
    SELECT cte.firstId, cte.firstName, 
           tab2.id, TRIM(tab2.Name, '>'), tab2.GrandParentID, 
           CASE WHEN SUBSTR(tab2.Name, 0, 2)='>' 
                THEN '>' || tab2.Description
           END AS Description, NULL
    FROM       cte 
    INNER JOIN tab2
            ON cte.ParentIDFromTable2 = tab2.id
), cte2 AS (
    SELECT firstId, firstName, name, lastId,
           FIRST_VALUE(Description) OVER(
               PARTITION BY firstId 
               ORDER     BY Description IS NULL,
                            SUBSTR(name, 0, 2)='>' DESC, 
                            lastid DESC
           ) AS Description, 
           Location
    FROM cte 
    ORDER BY firstId, ParentIDFromTable2
), cte3 AS (
    SELECT firstId                 AS id, 
           firstName               AS name,
           MIN(Description)        AS description,
           MAX(Location)           AS location,
           GROUP_CONCAT(name, '/') AS lineage
    FROM cte2 
    GROUP BY firstId, 
             firstName
)
SELECT cte3.id, 
       cte3.name, 
       tab1.weight, 
       tab1.height, 
       TRIM(cte3.description, '>') AS description, 
       tab3.Name || '/' || cte3.lineage AS lineage
FROM       cte3
INNER JOIN tab1 
        ON tab1.id = cte3.id
INNER JOIN tab3
        ON tab1.LocationIDFromTable3 = tab3.id

查看演示here

2j4z5cfb

2j4z5cfb2#

你需要一个递归的CTE和聚合,利用SQLite的裸列特性:

WITH cte AS (
  SELECT id, Name, Weight, Height, Description, Name Lineage, ParentIDFromTable2, LocationIDFromTable3, 
             false flag, 0 level 
  FROM Table1
  UNION ALL
  SELECT c.id, c.Name, c.Weight, c.Height, 
         CASE WHEN NOT flag AND t2.Name LIKE '>%' THEN t2.Description ELSE c.Description END, 
         TRIM(t2.Name, '>') || '/' || c.Lineage,
         t2.GrandParentID,
         c.LocationIDFromTable3,
         flag OR t2.Name LIKE '>%',
         c.level + 1
  FROM Table2 t2 INNER JOIN cte c 
  ON c.ParentIDFromTable2 = t2.id
)
SELECT t.id, t.Name, t.Weight, t.Height, t.Description, 
       t3.Name || '/' || t.Lineage Lineage 
FROM (SELECT *, MAX(level) FROM cte GROUP BY id) t 
INNER JOIN Table3 t3
ON t3.id = t.LocationIDFromTable3
GROUP BY t.id;

如果char '>'可能位于Table2Name的值内的任何位置,而不仅仅是在开始处,则将t2.Name LIKE '>%'替换为INSTR(t2.Name, '>')
参见demo

相关问题