英文:
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:
-
In Table2, the Location Column holds the IDs of Table3 - thus for clarity I've update the Location column to IDFromTable3 in Table2
-
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
-
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;
如果字符'>'
可能出现在Table2
的Name
值的任何位置而不仅仅是开头,那么请将 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 '>'
may be anywhere inside the value of Name
in Table2
and not only at the beginning, then replace t2.Name LIKE '>%'
with INSTR(t2.Name, '>')
.<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>
-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, '>'), 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
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, '>'), 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
Check the demo here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论