我想出了以下架构:
CREATE TABLE products
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
quantity INT(10) UNSIGNED NOT NULL,
purchase_price DECIMAL(8,2) NOT NULL,
sell_price DECIMAL(8,2) NOT NULL,
provider VARCHAR(255) NULL,
created_at TIMESTAMP NULL,
PRIMARY KEY (id)
);
# payment methods = {
# "0": "CASH",
# "1": "CREDIT CARD",
# ...
# }
CREATE TABLE orders
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
product_id INT(10) UNSIGNED NOT NULL,
quantity INT(10) UNSIGNED NOT NULL,
payment_method INT(11) NOT NULL DEFAULT 0,
created_at TIMESTAMP NULL,
PRIMARY KEY (id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
# status = {
# "0": "PENDING"
# "1": "PAID"
# }
CREATE TABLE invoices
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
price INT(10) UNSIGNED NOT NULL,
status INT(10) UNSIGNED NOT NULL DEFAULT 0,
created_at TIMESTAMP NULL,
PRIMARY KEY (id)
);
# payment methods = {
# "0": 'CASH',
# "1": 'CREDIT CARD',
# ...
# }
CREATE TABLE bills
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
payment_method INT(10) UNSIGNED NOT NULL DEFAULT 0,
price DECIMAL(8,2) NOT NULL,
created_at TIMESTAMP NULL,
PRIMARY KEY (id)
);
以下查询选择余额:
SELECT ((orders + invoices) - bills) as balance
FROM
(
SELECT SUM(p.sell_price * o.quantity) as orders
FROM orders o
JOIN products p
ON o.product_id = p.id
) orders,
(
SELECT SUM(price) as invoices
FROM invoices
WHERE status = 1
) invoices,
(
SELECT SUM(price) as bills
FROM bills
) bills;
它正在工作并返回正确的平衡,但我想使用Morris.js创建一个图表,我需要更改它以在给定的时间段内以这种格式返回每日或每月余额:
每日(2017-02-27至2017-03-01)
balance | created_at
--------------------------
600.00 | 2017-03-01
50.00 | 2017-02-28
450.00 | 2017-02-27
每月(2017-01至2017-03)
balance | created_at
--------------------------
200.00 | 2017-03
250.00 | 2017-02
350.00 | 2017-01
我需要在架构或查询中更改以这种方式返回结果?
http://sqlfiddle.com/#!9/2289a9/2
欢迎任何提示。提前致谢
答案 0 :(得分:1)
在SELECT列表中包含created_at
日期,在每个查询中包含GROUP BY子句。
抛弃旧学校逗号操作符以进行连接操作,并将其替换为LEFT JOIN。
要返回没有订单(或没有付款,或没有发票)的日期,我们需要一个单独的行来源,保证返回日期值。例如,我们可以使用内联视图:
SELECT d.created_dt
FROM ( SELECT '2017-02-27' + INTERVAL 0 DAY AS created_dt
UNION ALL SELECT '2017-02-28'
UNION ALL SELECT '2017-03-01'
) d
ORDER BY d.created_dt
内联视图只是一个选项。如果我们有一个calendar
表包含我们感兴趣的三个日期的行,我们可以使用它。重要的是我们有一个查询可以保证使用我们想要返回的不同created_at日期值返回给我们正好三行。
一旦我们有了这个,我们可以添加LEFT JOIN来获得该日期“账单”的价值。
SELECT d.created_dt
, b.bills
FROM ( SELECT '2017-02-27' + INTERVAL 0 DAY AS created_dt
UNION ALL SELECT '2017-02-28'
UNION ALL SELECT '2017-03-01'
) d
LEFT
JOIN ( SELECT DATE(bills.created_at) AS created_dt
, SUM(bills.price) AS bills
FROM bills
WHERE bills.created_at >= '2017-02-27'
AND bills.created_at < '2017-03-01' + INTERVAL 1 DAY
GROUP BY DATE(bills.created_at)
) b
ON b.created_dt = d.created_dt
ORDER BY d.created_dt
将其扩展为添加另一个LEFT JOIN,以获取发票
SELECT d.created_dt
, i.invoices
, b.bills
FROM ( SELECT '2017-02-27' + INTERVAL 0 DAY AS created_dt
UNION ALL SELECT '2017-02-28'
UNION ALL SELECT '2017-03-01'
) d
LEFT
JOIN ( SELECT DATE(bills.created_at) AS created_dt
, SUM(bills.price) AS bills
FROM bills
WHERE bills.created_at >= '2017-02-27'
AND bills.created_at < '2017-03-01' + INTERVAL 1 DAY
GROUP BY DATE(bills.created_at)
) b
ON b.created_dt = d.created_dt
LEFT
JOIN ( SELECT DATE(invoices.created_at) AS created_dt
, SUM(invoices.price) AS invoices
FROM invoices
WHERE invoices.status = 1
AND invoices.created_at >= '2017-02-27'
AND invoices.created_at < '2017-03-01' + INTERVAL 1 DAY
GROUP BY DATE(invoices.created_at)
) i
ON i.created_dt = d.created_dt
ORDER BY d.created_dt
同样,我们可以LEFT JOIN到另一个内联视图,返回按DATE(created_at)分组的总orders
。
内联视图返回created_dt
的不同值非常重要,每个日期值都有一行。
请注意,对于开发,测试和调试,我们可以独立执行内联视图查询。
当从LEFT JOIN没有返回匹配的行时,例如由于在该日期没有发票而没有从i
返回匹配的行,查询将为表达式{{1返回NULL }}。要用零替换NULL,我们可以使用i.invoices
函数或更多ANSI标准IFNULL
函数。例如:
COALESCE
要每月获得结果,我们需要一个每月返回一行的日历查询。我们假设我们将返回一个DATE值作为该月的第一天。例如:
SELECT d.created_dt
, IFNULL(i.invoices,0) AS invoices
, COALESCE(b.bills,0) AS bills
FROM ...
内联视图查询需要SELECT d.created_month
FROM ( SELECT '2017-02-01' + INTERVAL 0 DAY AS created_month
UNION ALL SELECT '2017-03-01'
) d
ORDER BY d.created_month
,因此它们会为每个月的每个值返回一个值。我的偏好是使用GROUP BY created_month
函数返回从DATE_FORMAT
派生的月份的第一天。但还有其他方法可以做到这一点。目标是为“2017-02-01”返回单行,为“2017-03-01”返回单行。请注意,created_at
上的日期范围从'2017-02-01'延伸到(但不包括)'2017-04-01',因此我们获得整月的总数。
created_at