将子查询转换为 sqlite 中的左连接
Translating subquery to left join in sqlite
我有一个 运行 针对 SQLite 数据库的查询,该数据库使用几个子查询。为了适应一些新的要求,我需要将其翻译为使用连接。以下是原始查询的结构版本:
SELECT c.id AS category_id, b.budget_year,
(
SELECT sum(actual)
FROM lines l1
WHERE status = 'complete'
AND category_id = c.id
AND billing_year = b.budget_year
) AS actual
(
SELECT sum(planned)
FROM lines l2
WHERE status IN ('forecasted', 'in-progress')
AND category_id = c.id
AND billing_year = b.budget_year
) AS rough_proposed
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
GROUP BY c.id, b.budget_year;
下一个查询是我第一次尝试将其转换为使用 LEFT OUTER JOIN
s:
SELECT c.id AS category_id, b.budget_year, sum(l1.actual) AS actual, sum(l2.planned) AS planned
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
LEFT OUTER JOIN lines AS l1 ON (l1.category_id = c.id
AND l1.billing_year = b.budget_year
AND l1.status = 'complete')
LEFT OUTER JOIN lines AS l2 ON (l2.category_id = c.id
AND l2.billing_year = b.budget_year
AND l2.status IN ('forecasted', 'in-progress'))
GROUP BY c.id, b.budget_year;
但是,actual
和 rough_proposed
列比预期的要大很多。我不是 SQL 专家,我很难理解这里发生了什么。有没有直接的方法将子查询转换为连接?
您的两个查询都有问题。但是,第一个查询隐藏了问题,而第二个查询使其可见。
事情是这样的:您加入 lines
两次 - 一次是 l1
,另一次是 l2
。当同时存在实际行和预测/进行中的行时,分组前的查询将多次使用同一行。发生这种情况时,每一行都会被计算多次,从而导致值膨胀。
第一个查询隐藏了这一点,因为它没有对 actual
和 rough_proposed
列应用聚合。 SQLite 为每个组选择第一个条目,它具有正确的值。
您可以通过仅加入行一次并有条件地计算金额来修复您的查询,如下所示:
SELECT
c.id AS category_id
, b.budget_year
, SUM(CASE WHEN l.status = 'complete' THEN l.actual END) AS actual
, SUM(CASE WHEN l.status IN ('forecasted', 'in-progress') THEN l.planned END) AS planned
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
LEFT OUTER JOIN lines AS l ON (l.category_id = c.id AND l1.billing_year = b.budget_year)
GROUP BY c.id, b.budget_year
在这个新查询中,来自 lines
的每一行只引入一次;在 actual
/planned
列之一中计算它的决定是在 SUM
聚合函数中嵌入的条件表达式中做出的。
我有一个 运行 针对 SQLite 数据库的查询,该数据库使用几个子查询。为了适应一些新的要求,我需要将其翻译为使用连接。以下是原始查询的结构版本:
SELECT c.id AS category_id, b.budget_year,
(
SELECT sum(actual)
FROM lines l1
WHERE status = 'complete'
AND category_id = c.id
AND billing_year = b.budget_year
) AS actual
(
SELECT sum(planned)
FROM lines l2
WHERE status IN ('forecasted', 'in-progress')
AND category_id = c.id
AND billing_year = b.budget_year
) AS rough_proposed
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
GROUP BY c.id, b.budget_year;
下一个查询是我第一次尝试将其转换为使用 LEFT OUTER JOIN
s:
SELECT c.id AS category_id, b.budget_year, sum(l1.actual) AS actual, sum(l2.planned) AS planned
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
LEFT OUTER JOIN lines AS l1 ON (l1.category_id = c.id
AND l1.billing_year = b.budget_year
AND l1.status = 'complete')
LEFT OUTER JOIN lines AS l2 ON (l2.category_id = c.id
AND l2.billing_year = b.budget_year
AND l2.status IN ('forecasted', 'in-progress'))
GROUP BY c.id, b.budget_year;
但是,actual
和 rough_proposed
列比预期的要大很多。我不是 SQL 专家,我很难理解这里发生了什么。有没有直接的方法将子查询转换为连接?
您的两个查询都有问题。但是,第一个查询隐藏了问题,而第二个查询使其可见。
事情是这样的:您加入 lines
两次 - 一次是 l1
,另一次是 l2
。当同时存在实际行和预测/进行中的行时,分组前的查询将多次使用同一行。发生这种情况时,每一行都会被计算多次,从而导致值膨胀。
第一个查询隐藏了这一点,因为它没有对 actual
和 rough_proposed
列应用聚合。 SQLite 为每个组选择第一个条目,它具有正确的值。
您可以通过仅加入行一次并有条件地计算金额来修复您的查询,如下所示:
SELECT
c.id AS category_id
, b.budget_year
, SUM(CASE WHEN l.status = 'complete' THEN l.actual END) AS actual
, SUM(CASE WHEN l.status IN ('forecasted', 'in-progress') THEN l.planned END) AS planned
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
LEFT OUTER JOIN lines AS l ON (l.category_id = c.id AND l1.billing_year = b.budget_year)
GROUP BY c.id, b.budget_year
在这个新查询中,来自 lines
的每一行只引入一次;在 actual
/planned
列之一中计算它的决定是在 SUM
聚合函数中嵌入的条件表达式中做出的。