【问题标题】:Combining three tables when two have the same foreign key当两个表具有相同的外键时组合三个表
【发布时间】:2019-08-31 15:29:51
【问题描述】:

您好,我正在制作一些会计软件,允许您存储多个月的交易(存储在表 transactions 中)并根据可用类别列表对这些交易进行分类(存储在表 categories 中)。最后每个月,用户可以创建一个预算,在其中构建一个列表(来自categories 的子集)并为每个条目分配一个目标。这些列表存储在表budget 中。

我需要一个查询来返回指定月份的支出和预算摘要。此摘要将是一个包含 categories.name、budget.goal 和 sum(transactions.amount) 的表。

有时,用户可能拥有指定月份的预算项目,但尚未与该类别进行任何交易(Out to Eat 示例)。有时,用户会出现他们没有预算的意外费用(汽车维修示例),并且在某些类别(如假期)中,用户没有为该项目预算并且没有该类别的费用。

SELECT categories.id, categories.name, SUM(transactions.amount) FROM categories LEFT JOIN transactions ON categories.id=transactions.category_id WHERE transactions.date LIKE '2019-08-%' GROUP BY categories.id;

得到我想要的一半,并且

SELECT categories.id, categories.name, budgets.goal FROM categories LEFT JOIN budgets ON categories.id=budgets.category_id WHERE budgets.date LIKE '2019-08-%' GROUP BY categories.id;

得到我想要的另一半。是否有一个查询可以返回如我上图所示的结果?如果我们可以排除目标和总和均为 NULL 的结果,我会更加激动。

【问题讨论】:

  • 同一类别可以有多个同一月份的butgets吗?
  • 了解 LEFT JOIN ON 返回的内容:INNER JOIN ON rows UNION ALL 不匹配的左表行,由 NULL 扩展。作为 OUTER JOIN 的一部分,始终知道您想要什么 INNER JOIN。 WHERE 或 INNER JOIN ON 在 OUTER JOIN ON 删除任何由 NULL 扩展的行后,需要右 [sic] 表列不为 NULL,即只留下 INNER JOIN ON 行,即“将 OUTER JOIN 转换为 INNER JOIN”。你有那个。
  • 请在代码问题中给出minimal reproducible example--剪切&粘贴&运行代码;具有期望和实际输出的示例输入(包括逐字错误消息);标签和版本;明确的规范和解释。这包括您可以提供的最少代码,即您显示的代码可以通过您显示的代码扩展为不正常。 (调试基础。)

标签: mysql join foreign-keys relational-database primary-key


【解决方案1】:

如果您知道每个类别和每个月最多只能有一个预算,那么您可以(左)将两个(子)表加入到categories

SELECT 
  c.id,
  categories.name,
  MAX(b.goal) as budget_goal,
  SUM(t.amount) as total_txn_amount
FROM categories c
LEFT JOIN budgets b
  ON  c.id = b.category_id 
  AND b.date LIKE '2019-08-%'
LEFT JOIN transactions t
  ON c.id = t.category_id 
GROUP BY categories.id
HAVING COALESCE(budget_goal, total_txn_amount) IS NOT NULL;

请注意,虽然我们知道每个组只能有一个预算,但引擎不会,并且可能会声称 b.goal 必须在 GROUP BY 子句中或在聚合函数中使用。所以我们使用MAX(b.goal) 来避免这个错误。

为了提高第一个 JOIN 的性能,我会改变

AND b.date LIKE '2019-08-%'

AND b.date >= '2019-08-01'
AND b.date <  '2019-08-01' + INTERVAL 1 MONTH

并在(category_id, date) 上创建复合索引。

为了强制 budgets 表中类别和月份组合的唯一性,我会创建一个虚拟列,如

year_month VARCHAR(7) as (LEFT(date, 7))

(category_id, year_month)上的唯一键

然后就可以使用了

LEFT JOIN budgets b
  ON  c.id = b.category_id 
  AND b.date = '2019-08'

【讨论】:

  • 效果很好,谢谢。我在将两个表连接到同一个主键时度过了一段糟糕的时光。我不知道我遗漏了什么,但我不断收到错误,所有在线示例都显示加入“c to b then b to a”等等。我担心这是不可能的。做得好,谢谢!
【解决方案2】:

您可以尝试以下查询以找到正确的结果:

SELECT c.name,b.goal,sum(ct.amount) FROM era.categories c left join budget b on b.cat_id=c.id left join transactions ct on ct.cat_id=c.id WHERE b.date LIKE '2019-08-%' group by ct.cat_id;

谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 2019-05-03
    • 2014-11-16
    • 2019-05-24
    • 1970-01-01
    • 2018-01-13
    相关资源
    最近更新 更多