SQLite - 在具有多个潜在中间关系的 2 个表之间聚合相关数据(树状)

标签 sql sqlite join aggregate-functions multiple-tables

我有一个 SQLite 数据库(v. 3.8.1),其架构有些不寻常,无法更改。

对于这个问题,有 5 个表(t1 到 t5),我需要使用来自 t1 和 t5 的数据创建一个汇总报告,但是我需要在 t5 中引用的数据只能根据与记录的关系来收集在 t1 到 t4。

为了帮助澄清 - 假设 t1 保存有关文档的数据。文档随后可以再经历 1 到 4 次迭代(在每次迭代中都有不同的字段可用,因此有 5 个不同的表,而不仅仅是 1 个表中的一个标志来表示它处于什么迭代)。

我对初始记录/文档(保存在 t1 中)是否已经到达它的最终迭代感兴趣(在 t5 中存在一个 ParentGUID,当跟踪表链时,最终到达 t1 或没有)。

t1 有一个 GUID(文本)字段,t2 到 t5 有 GUID 和 ParentGUID 字段(也是文本)。 t2 到 t5 中的 ParentGUID 字段不必填充(在某些情况下可以跳过文档迭代),但是当 ParentGUID 具有值时,它将始终是前一个表中的 GUID(例如,如果 t5 具有 ParentGuid值,它将是来自 t1、t2、t3 或 t4 的 GUID)。

这意味着我想要来自 t1 的所有不同记录,然后对于每个来自 t5 的值(或多个值)(如果存在),或者 null 如果不存在。

如果 t5 记录中的 ParentGuid 字段值是 t4 中记录的 GUID,并且该 t4 记录中的 ParentGuid 字段值是 t1 中记录的 GUID,则认为该特定 t1 记录已达到其最终迭代。

同样,ParentGUID > GUID 链接将被视为 t1 > t5,初始 > 最终迭代包括:

t1 > t2 > t3 > t4 > t5
t1 > t2 > t3 > t5
t1 > t2 > t4 > t5
t1 > t2 > t5
t1 > t3 > t4 > t5
t1 > t3 > t5
t1 > t4 > t5
t1 > t5

或以图形表示:

Possible relationship paths from T1 to T5

考虑以下测试模式:
CREATE TABLE Table1
    ("GUID" TEXT, "Name" TEXT)
;

CREATE TABLE Table2
    ("GUID" TEXT, "ParentGUID" TEXT)
;

CREATE TABLE Table3
    ("GUID" TEXT, "ParentGUID" TEXT)
;

CREATE TABLE Table4
    ("GUID" TEXT, "ParentGUID" TEXT)
;

CREATE TABLE Table5
    ("GUID" TEXT, "Name" TEXT, "Amount" REAL, "ParentGUID" TEXT)
;

INSERT INTO Table1
    ("GUID", "Name")
VALUES
    ('ABC', 'A1')
;


INSERT INTO Table1
    ("GUID", "Name")
VALUES
    ('DEF', 'A2')
;

INSERT INTO Table1
    ("GUID", "Name")
VALUES
    ('GHI', 'A3')
;

INSERT INTO Table2
    ("GUID", "ParentGUID")
VALUES
    ('JKL', 'GHI')
;

INSERT INTO Table2
    ("GUID", "ParentGUID")
VALUES
    ('MNO', '')
;

INSERT INTO Table2
    ("GUID", "ParentGUID")
VALUES
    ('PQR', 'GHI')
;

INSERT INTO Table3
    ("GUID", "ParentGUID")
VALUES
    ('STU', 'MNO')
;

INSERT INTO Table3
    ("GUID",  "ParentGUID")
VALUES
    ('STU', 'GHI')
;

INSERT INTO Table3
    ("GUID", "ParentGUID")
VALUES
    ('VWX', 'PQR')
;


INSERT INTO Table4
    ("GUID", "ParentGUID")
VALUES
    ('YZA', 'VWX')
;

INSERT INTO Table4
    ("GUID", "ParentGUID")
VALUES
    ('BCD', '')
;

INSERT INTO Table4
    ("GUID", "ParentGUID")
VALUES
    ('EFG', 'GHI')
;

INSERT INTO Table5
    ("GUID", "ParentGUID", "Amount", "Name" )
VALUES
    ('HIJ', 'EFG', -500, 'E3')
;


INSERT INTO Table5
    ("GUID", "ParentGUID", "Amount", "Name" )
VALUES
    ('KLM', 'YZA', -702, 'E2')
;


INSERT INTO Table5
    ("GUID", "ParentGUID", "Amount", "Name" )
VALUES
    ('NOP', '', 220, 'E8')
;

INSERT INTO Table5
    ("GUID", "ParentGUID", "Amount", "Name" )
VALUES
    ('QRS', 'GHI', 601, 'E4')
;

我想要做的是获取 t1 中的所有记录,然后显示 t5 中所有相关 Amount 字段的总数(以上面列出的任何方式相关),以及 t5 中所有相关 Name 字段的 group_concat。

使用上面的示例模式,它看起来像:
t1.Name   total(t5.Amount)   group_concat(t5.Name)
--------------------------------------------------
A1                   0.00  
A2                   0.00  
A3                -601.00    E2,E3,E4

我尝试了一堆不同的连接,但没有任何效果......要么我的 Total/Group_Concat 单元格中的项目太多(由于多次添加项目,总数太高,并且有多个重复名称,例如“E4,E4 ,E4,E4,E2,E3,E3,E4,E4..."),或者我只能从 t5 (601.00, E4) 获得一个直接链接到 t1 的项目。

例如,查询只为我提供了 t1 记录 GHI 的 E4/601.00 结果:
SELECT DISTINCT t1.guid "OriginalGuid", t1.name "OriginalName", TOTAL(t5."Amount") as "TotalAmount", group_concat(t5.Name) AS "FinalNames"
FROM 
Table1 t1
LEFT  JOIN Table5 t5 ON (t1.GUID=t5.ParentGUID)
LEFT  JOIN Table4 t4 ON (t1.GUID=t4.ParentGuid AND t5.ParentGuid=t4.Guid)
LEFT  JOIN Table3 t3 ON (t1.GUID=t3.ParentGuid AND (t4.ParentGuid=t3.Guid OR t5.ParentGuid=t3.Guid))
LEFT  JOIN Table2 t2 ON (t1.GUID=t2.ParentGuid AND (t3.ParentGuid=t2.Guid AND ((t4.ParentGuid=t3.Guid And t5.ParentGuid=t4.guid) or (t5.ParentGuid=t3.Guid)) OR (t4.ParentGuid=t2.Guid and t5.ParentGuid=t4.Guid) OR (t5.ParentGuid=t2.Guid)))
GROUP BY t1.GUID;

可悲的是,我周末的大部分时间都在研究这个,但我一直无法找出一些有效且性能合理的东西(我有一些似乎对小数据集工作正常但花了几分钟的时间我的完整数据集,太长了 - 尽管不幸的是,我已经丢失了 SQL)。

我现在正在继续研究解决方案,如果我找到它,我会在这里发布答案,但如果他们在那里,我将不胜感激任何帮助/想法!

这是我的 SQL fiddle :http://sqlfiddle.com/#!5/1a2ac/55

在此先感谢您的帮助。

最佳答案

cha 的回答没问题,但可以通过添加临时表来存储从 table2 到 table5 的所有关系来优化。

CREATE TABLE TableRel
    ("GUID" TEXT, "ParentGUID" TEXT, "TB" TEXT);

insert into TableRel
select GUID, ParentGUID, 'TABLE2'
FROM TABLE2
UNION ALL
select GUID, ParentGUID, 'TABLE3'
FROM TABLE3
UNION ALL
select GUID, ParentGUID, 'TABLE4'
FROM TABLE4
UNION ALL
select GUID, ParentGUID, 'TABLE5'
FROM TABLE5
;

更新

然后您可以使用递归查询从 table1 中获取所有后代。
WITH RECURSIVE Table1Descendants(GUID, DescendantGUID,generation) as (
  select t1.GUID, Rel.GUID ,1
  from Table1 t1
  inner join TableRel rel
  on t1.GUID= Rel.ParentGUID
  UNION ALL
  select td.GUID, Rel.GUID, td.generation+1
  from TableRel Rel
  inner join Table1Descendants td
  on td.DescendantGUID= Rel.ParentGUID
  ) 
select t1.guid , t1.name , coalesce(sum(t5.Amount) ,0)
from Table1 as t1
left join Table1Descendants
on t1.GUID = Table1Descendants.GUID
left join Table5 as t5
on t5.GUID = Table1Descendants.DescendantGUID
group by t1.guid,t1.name
order by t1.name;

或者您可以从 table5 中获取所有祖先。
WITH RECURSIVE Table1Ancestors(GUID, AncestorGUID) as (
  select t5.GUID, Rel.ParentGUID 
  from Table5 t5
  inner join TableRel rel
  on t5.GUID= Rel.GUID
  UNION ALL
  select ta.GUID, Rel.ParentGUID
  from TableRel Rel
  inner join Table1Ancestors ta
  on ta.AncestorGUID= Rel.GUID
  ) 
select t1.guid , t1.name , coalesce(sum(t5.Amount) ,0)
from Table1 as t1
left join Table1Ancestors
on t1.GUID = Table1Ancestors.AncestorGUID
left join Table5 as t5
on t5.GUID = Table1Ancestors.GUID
group by t1.guid,t1.name
order by t1.name;

但只有 3.8.3 SQLite 支持递归 CTE,我没有这个版本的 SQLite,这里是 SQLFidle用 PostgreSQL 测试,它们的语法与 recursive query 相似。 ,但没有 totalgroup_concat PostgreSQL 中的函数。

这是一个非递归查询( SqlFiddle ),以防您没有 SQLite 3.8.3 或更高版本:
select t1.guid "OriginalGuid", t1.name "OriginalName", TOTAL(t5."Amount") as "TotalAmount", group_concat(t5.Name) AS "FinalNames"
from Table1 as t1
left join
(
  select t1.GUID, Rel.GUID as DescendantGUID, 1
  from Table1 t1
  inner join TableRel rel
  on t1.GUID= Rel.ParentGUID
  UNION ALL
  select t1.GUID, Rel2.GUID, 2
  from Table1 t1
  inner join TableRel rel1
  on t1.GUID= Rel1.ParentGUID
  inner join TableRel rel2
  on Rel1.GUID= Rel2.ParentGUID
  UNION ALL
  select t1.GUID, Rel3.GUID, 3
  from Table1 t1
  inner join TableRel rel1
  on t1.GUID= Rel1.ParentGUID
  inner join TableRel rel2
  on Rel1.GUID= Rel2.ParentGUID
  inner join TableRel rel3
  on Rel2.GUID= Rel3.ParentGUID
  UNION ALL
  select t1.GUID, Rel4.GUID, 4
  from Table1 t1
  inner join TableRel rel1
  on t1.GUID= Rel1.ParentGUID
  inner join TableRel rel2
  on Rel1.GUID= Rel2.ParentGUID
  inner join TableRel rel3
  on Rel2.GUID= Rel3.ParentGUID
  inner join TableRel rel4
  on Rel3.GUID= Rel4.ParentGUID
  ) as Table1Descendants
on t1.GUID = Table1Descendants.GUID
left join Table5 as t5
on t5.GUID = Table1Descendants.DescendantGUID
group by t1.guid,t1.name

结果:
OriginalGuid    OriginalName    TotalAmount FinalNames
ABC             A1              0.0 
DEF             A2              0.0 
GHI             A3              -601.0      E3,E2,E4

关于SQLite - 在具有多个潜在中间关系的 2 个表之间聚合相关数据(树状),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25476655/

相关文章:

android - 将 MySQL 数据库导入到 android SQLite 数据库

Python SQLite3 获取原始数据?

.net - LINQ Join 运算符是否使用嵌套循环、合并或 HashSet 连接?

sql - "INSERT INTO SELECT"与 "INSERT INTO VALUES ... (SELECT)"

mysql - 将一列移动到另一个表并通过外部 ID 更新该列

mysql - SQL触发器语法

android - 如何通过将 map 图 block 图像保存到 sqlite 数据库中来使用 osmdroid 实现离线 map ?

mysql - 在这种情况下如何在 SQL 中使用 GROUP BY 和 COUNT

mysql - 简单的 MySQL 连接不起作用

sql - 一个 SQLite 查询中的两个总和和三个表