在多个表格上进行有条件的递归查找。

huangapple go评论73阅读模式
英文:

Recursive lookup on Multiple Tables with conditional

问题

Table1 中的每个项目都应返回完整的父层次结构,以及来自 Table2 的描述(如果 Table2 的 Name 字段包含 > 字符);只使用带有 > 字符的 Name 字段的第一个描述。例如:

ID Name Weight Height Description Lineage
1 Jack 180 183 Des5 P1/Adam/Zack/Noah/Jacob/Jeff/Jack
4 Jill 110 140 T1Desc4 P2/Adam/Jill
5 Juliet 122 150 Des2 P1/Adam/Zack/Noah/Kanye/Juliet
英文:

I have multiple tables from which I need to extract Parent/Child info for each item in one of the tables. So for example:

Table1:

id Name ParentIDFromTable2 Weight Height Description LocationIDFromTable3
1 Jack 5 180 183 T1Desc1 1
2 Sparrow 3 210 169 T1Desc2 1
3 John 6 350 210 T1Desc3 1
4 Jill 1 110 140 T1Desc4 2
5 Juliet 7 122 150 T1Desc5 1

Table2:

id Name GrandParentID IDFromTable3 Description
1 Adam 0 1 Des1
2 >Zack 1 1 Des2
3 Noah 2 1 Des3
4 Jacob 3 1 Des4
5 >Jeff 4 1 Des5
6 Drake 5 1 Des6
7 Kanye 3 1 Des7

Table3:

id Name Description
1 P1 D1
2 P2 D2
3 P3 D3

So for each item in Table1, it should return the full parental hierarchy along with the description from Table2 IF the Name field in Table2 contains > character AND Only use the description from the first Name field with the > character. So for example:

ID Name Weight Height Description Lineage
1 Jack 180 183 Des5 P1/Adam/Zack/Noah/Jacob/Jeff/Jack
4 Jill 110 140 T1Desc4 P2/Adam/Jill
5 Juliet 122 150 Des2 P1/Adam/Zack/Noah/Kanye/Juliet

I'd asked a similar question and thought I would be able to figure out the conditional description but no luck so far.

I would greatly appreciate your help!

[UPDATE]
For clarification:

  1. In Table2, the Location Column holds the IDs of Table3 - thus for clarity I've update the Location column to IDFromTable3 in Table2

  2. the challenge for me is to introduce a check while doing recursion such that IF there exists in Table2 Name column a '>' character, then the item in Table1's Description shall be updated to the description from Table2's first occurrence (of > character - if for example during recursion on Table2 more than 1 GrandParentID items are encountered that have '>' character in the Name column) else it shall use the description from its own (Table1) row

  3. I've fixed the Table1 to reflect the updated column LocationIDFromTable3 -- so in other words we can use the Table3 ID directly from Table1 instead of going to Table2

答案1

得分: 2

需要使用递归公共表表达式(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 INSTR(t2.Name, '>') > 0 THEN t2.Description ELSE c.Description END, 
         TRIM(t2.Name, '>') || '/' || c.Lineage,
         t2.GrandParentID,
         c.LocationIDFromTable3,
         flag OR INSTR(t2.Name, '>') > 0,
         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;

如果字符'>'可能出现在Table2Name值的任何位置而不仅仅是开头,那么请将 t2.Name LIKE '>% 替换为 INSTR(t2.Name, '>')

查看演示

英文:

You need a recursive CTE and aggregation utilising SQLite's feature of bare columns:

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;

If the char &#39;&gt;&#39; may be anywhere inside the value of Name in Table2 and not only at the beginning, then replace t2.Name LIKE &#39;&gt;%&#39; with INSTR(t2.Name, &#39;&gt;&#39;).<br/>

See the demo.<br/>

答案2

得分: 1

以下是您提供的内容的中文翻译:

这是使用递归的一种方法:

  • 基本步骤”:从tab1收集信息
  • 递归步骤”:将步骤n-1的cte与tab2上匹配的cte.ParentIDFromTable2 = tab2.id连接。通过在开头连接相同的字符,使>前缀的名称具有可识别的描述。

一旦递归完成,您可以:

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

每个子查询中都执行。

输出

id name weight height description lineage
1 Jack 180 183 Des5 P1/Adam/Zack/Noah/Jacob/Jeff/Jack
2 Sparrow 210 169 Des2 P1/Adam/Zack/Noah/Sparrow
3 John 350 210 Des5 P1/Adam/Zack/Noah/Jacob/Jeff/Drake/John
4 Jill 110 140 T1Desc4 P1/Adam/Jill
5 Juliet 122 150 Des2 P1/Adam/Zack/Noah/Kanye/Juliet

查看演示此处

编辑:尝试删除一些要在最后添加的列的示例是:

  • 在递归cte中保留所有必要的要更新的列,
  • 添加最后一个连接操作以恢复您丢失的字段

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

输出

查看演示此处

英文:

Here's one way to do it with recursion:

  • "Base step": gather information from tab1
  • "Recursive step": join step n-1 cte with tab2 on matching cte.ParentIDFromTable2 = tab2.id. Make &gt;-prefixed names to have their description recognizable, by concatenating the very same character at the beginning.

Once recursion is done, you can:

  • gather the first relevant description with FIRST_VALUE, and order records to...
  • make aggregation using GROUP_CONCAT on names, to get your lineage
  • lastly join with tab3 to gather ancestor infos

each carried out in a subquery.

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, &#39;&gt;&#39;), tab2.GrandParentID, NULL, NULL, 
           CASE WHEN SUBSTR(tab2.Name, 0, 2)=&#39;&gt;&#39; 
                THEN &#39;&gt;&#39; || 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)=&#39;&gt;&#39; 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, &#39;/&#39;) AS lineage
    FROM cte2 
    GROUP BY firstId, 
             firstName
)
SELECT cte3.id, 
       cte3.name, 
       cte3.weight, 
       cte3.height, 
       TRIM(cte3.description, &#39;&gt;&#39;) AS description, 
       tab3.name || &#39;/&#39; || cte3.lineage AS lineage
FROM       cte3
INNER JOIN tab3
        ON tab3.id = cte3.location

Output:

id name weight height description lineage
1 Jack 180 183 Des5 P1/Adam/Zack/Noah/Jacob/Jeff/Jack
2 Sparrow 210 169 Des2 P1/Adam/Zack/Noah/Sparrow
3 John 350 210 Des5 P1/Adam/Zack/Noah/Jacob/Jeff/Drake/John
4 Jill 110 140 T1Desc4 P1/Adam/Jill
5 Juliet 122 150 Des2 P1/Adam/Zack/Noah/Kanye/Juliet

Check the demo here.


EDIT: An attempt at removing some of the columns to be added at the end is the following:

  • keep all the necessary, to be updated, columns in the recursion cte,
  • add a last join operation to recover the fields you were missing

In the following example, we're going to remove the fields "weight" and "height", to add them in the last step.

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, &#39;&gt;&#39;), tab2.GrandParentID, 
           CASE WHEN SUBSTR(tab2.Name, 0, 2)=&#39;&gt;&#39; 
                THEN &#39;&gt;&#39; || 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)=&#39;&gt;&#39; 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, &#39;/&#39;) AS lineage
    FROM cte2 
    GROUP BY firstId, 
             firstName
)
SELECT cte3.id, 
       cte3.name, 
       tab1.weight, 
       tab1.height, 
       TRIM(cte3.description, &#39;&gt;&#39;) AS description, 
       tab3.Name || &#39;/&#39; || cte3.lineage AS lineage
FROM       cte3
INNER JOIN tab1 
        ON tab1.id = cte3.id
INNER JOIN tab3
        ON tab1.LocationIDFromTable3 = tab3.id

Check the demo here.

huangapple
  • 本文由 发表于 2023年5月24日 21:35:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324144.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定