您好,我正在制作一些会计软件,该软件可让您跨月存储交易(存储在表transactions
中),并根据可用类别列表对这些交易进行分类(存储在表categories
中) 。最终,用户每个月都可以创建预算,在该预算中,他们可以建立列表(来自categories
的子集),并为每个条目分配目标。这些列表存储在表budget
中。
我需要一个查询,该查询返回指定月份的支出和预算摘要。该摘要将是类别,名称,预算,目标和sum(transactions.amount)的表格。
有时,用户会有指定月份的预算项目,但尚未进行该类别的任何交易(“吃饭”示例)。有时,用户会有一笔他们没有预算的意外支出(“汽车维修示例”),并且在某些类别(例如度假)中,用户没有对该项目进行预算,并且没有该类别的支出。
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的结果,我会更加兴奋。
答案 0 :(得分: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'
答案 1 :(得分:0)
您可以尝试在下面的查询中找到合适的结果:
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;
谢谢。