【问题标题】:SQLite Sum over children in self-referencing table自引用表中子项的 SQLite 总和
【发布时间】:2021-12-04 12:37:50
【问题描述】:

我有一个ecosystems 表,它是自引用的,因为每个生态系统都可以有子生态系统。每个生态系统也可以有一个分数(代表生态系统的健康程度)。这些列是(带有示例数据):

| slug | parent_slug | full_slug   | score |
| ---- | ----------- | ----------- | ----- |
| aaa  | null        | aaa         | 1     |
| bbb  | aaa         | aaa/bbb     | 2     |
| ccc  | bbb         | aaa/bbb/ccc | 4     |
| ddd  | null        | ddd         | 8     |
| eee  | ddd         | ddd/eee     | 16    |
| fff  | null        | fff         | 32    |

full_slug 列代表从顶级生态系统向下的完整路径。它是多余的,因为它可以从 slugparent_slug 列中推断出来,但它就在那里。

我想要实现的是创建一个行数相同的查询,但有一列 total_score 以递归方式计算每个生态系统的分数加上其所有子生态系统的分数。也就是说,输出应该是:

| slug | total_score |
| ---- | ----------- |
| aaa  | 7           |
| bbb  | 6           |
| ccc  | 4           |
| ddd  | 24          |
| eee  | 16          |
| fff  | 32          |

我开始了以下查询:

WITH top AS (
    SELECT
        SUM(e.score) as total_score,
        CASE instr(e.full_slug, '/') WHEN 0 THEN
            e.full_slug
        ELSE
            substr(e.full_slug, 0, instr(e.full_slug, '/'))
        END AS top_level_eco
    FROM ecosystems e
    GROUP BY top_level_eco
)
SELECT
    e.slug,
    top.total_score
FROM ecosystems e
INNER JOIN top on top.top_level_eco = e.slug;

但不幸的是,它只显示了顶级生态系统及其总分。

【问题讨论】:

  • 蛞蝓可以包含/吗?是否有最大深度full_slug/的最大数量)
  • Slugs 只能是字母数字(这就是为什么选择/ 作为分隔符)。没有最大的生态系统深度。

标签: sql sqlite


【解决方案1】:

我能想到几个答案...

一般的答案是使用递归...

WITH
  tree AS
(
  SELECT
   slug   AS base_slug,
   slug   AS current_slug,
   score  AS score
  FROM
   ecosystems

  UNION ALL

  SELECT
    t.base_slug,
    e.slug,
    e.score
  FROM
    tree        t
  INNER JOIN
    ecosystems  e
      ON e.parent_slug = t.current_slug
)
SELECT
  base_slug    AS slug,
  SUM(score)   AS total_score
FROM
  tree
GROUP BY
  base_slug
ORDER BY
  base_slug

另一种选择是在JOIN 中使用full_slug,尽管这会禁止使用索引,并且通常比上述一般解决方案要慢得多。

SELECT
  e.slug,
  SUM(m.score)   AS total_score
FROM
  ecosystems    e
INNER JOIN
  ecosystems    m  -- members
    ON '/' || m.full_slug || '/' LIKE '%/' || e.slug || '/%'
GROUP BY
  e.slug
ORDER BY
  e.slug

第三种方法是unnest/explodefull_slug(即为full_slug的每个组件创建一行),然后按组件分组. SQLite 本身没有该功能,因此也可能通过递归解决。

WITH
  tree AS
(
  SELECT
    SUBSTR(full_slug || '/', 1, INSTR(full_slug || '/', '/')-1)   AS slug,
    SUBSTR(full_slug || '/',    INSTR(full_slug || '/', '/')+1)   AS path,
    score
  FROM
    ecosystems

  UNION ALL

  SELECT
    SUBSTR(path, 1, INSTR(path, '/')-1)   AS slug,
    SUBSTR(path,    INSTR(path, '/')+1)   AS path,
    score
  FROM
    tree
  WHERE
    tree.path <> ''
)
SELECT
  slug,
  SUM(score)    AS total_score
FROM
  tree
GROUP BY
  slug
ORDER BY
  slug

所有三种方法的演示:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 2013-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多