我正在尝试使用MySQL 5.1.45创建我认为是在线商店的相对基本的报告
商店可以接收多种货币付款。 我已经创建了一些带有数据的示例表,并且我试图生成一个按日期和货币分组的简单表格结果集,以便我可以绘制这些数字。
我想查看每个日期可用的每种货币,如果当天没有该货币的销售,则结果为0。 如果我可以开始工作,我想做同样的事情,但也按照产品ID进行分组。
在我提供的示例数据中,只有3种货币和2种产品ID,但实际上每种货币可以有任意数量。
我可以按日期正确分组,但是当我按货币添加分组时,我的查询不会返回我想要的内容。
我的工作基于this article。
我的报告查询,仅按日期分组:
SELECT calendar.datefield AS date,
IFNULL(SUM(orders.order_value),0) AS total_value
FROM orders
RIGHT JOIN calendar ON (DATE(orders.order_date) = calendar.datefield)
WHERE (calendar.datefield BETWEEN (SELECT MIN(DATE(order_date)) FROM orders) AND (SELECT MAX(DATE(order_date)) FROM orders))
GROUP BY date
现在按日期和货币分组:
SELECT calendar.datefield AS date, orders.currency_id,
IFNULL(SUM(orders.order_value),0) AS total_value
FROM orders
RIGHT JOIN calendar ON (DATE(orders.order_date) = calendar.datefield)
WHERE (calendar.datefield BETWEEN (SELECT MIN(DATE(order_date)) FROM orders) AND (SELECT MAX(DATE(order_date)) FROM orders))
GROUP BY date, orders.currency_id
我得到的结果(按日期和货币分组):
+------------+-------------+-------------+
| date | currency_id | total_value |
+------------+-------------+-------------+
| 2009-08-15 | 3 | 81.94 |
| 2009-08-15 | 45 | 25.00 |
| 2009-08-15 | 49 | 122.60 |
| 2009-08-16 | NULL | 0.00 |
| 2009-08-17 | 45 | 25.00 |
| 2009-08-17 | 49 | 122.60 |
| 2009-08-18 | 3 | 81.94 |
| 2009-08-18 | 49 | 245.20 |
+------------+-------------+-------------+
我想要的结果:
+------------+-------------+-------------+
| date | currency_id | total_value |
+------------+-------------+-------------+
| 2009-08-15 | 3 | 81.94 |
| 2009-08-15 | 45 | 25.00 |
| 2009-08-15 | 49 | 122.60 |
| 2009-08-16 | 3 | 0.00 |
| 2009-08-16 | 45 | 0.00 |
| 2009-08-16 | 49 | 0.00 |
| 2009-08-17 | 3 | 0.00 |
| 2009-08-17 | 45 | 25.00 |
| 2009-08-17 | 49 | 122.60 |
| 2009-08-18 | 3 | 81.94 |
| 2009-08-18 | 45 | 0.00 |
| 2009-08-18 | 49 | 245.20 |
+------------+-------------+-------------+
我在测试中使用的架构和数据:
CREATE TABLE orders
(
id INT PRIMARY KEY AUTO_INCREMENT,
order_date DATETIME,
order_id INT,
product_id INT,
currency_id INT,
order_value DECIMAL(9,2),
customer_id INT
);
INSERT INTO orders (order_date, order_id, product_id, currency_id, order_value, customer_id)
VALUES
('2009-08-15 10:20:20', '123', '1', '45', '12.50', '322'),
('2009-08-15 12:30:20', '124', '1', '49', '122.60', '400'),
('2009-08-15 13:41:20', '125', '1', '3', '40.97', '324'),
('2009-08-15 10:20:20', '126', '2', '45', '12.50', '345'),
('2009-08-15 13:41:20', '131', '2', '3', '40.97', '756'),
('2009-08-17 10:20:20', '3234', '1', '45', '12.50', '1322'),
('2009-08-17 10:20:20', '4642', '2', '45', '12.50', '1345'),
('2009-08-17 12:30:20', '23', '2', '49', '122.60', '3142'),
('2009-08-18 12:30:20', '2131', '1', '49', '122.60', '4700'),
('2009-08-18 13:41:20', '4568', '1', '3', '40.97', '3274'),
('2009-08-18 12:30:20', '956', '2', '49', '122.60', '3542'),
('2009-08-18 13:41:20', '443', '2', '3', '40.97', '7556');
CREATE TABLE currency
(
id INT PRIMARY KEY,
name VARCHAR(255)
);
INSERT INTO currency (id, name)
VALUES
(3, 'Euro'),
(45, 'US Dollar'),
(49, 'CA Dollar');
CREATE TABLE calendar (datefield DATE);
DELIMITER |
CREATE PROCEDURE fill_calendar(start_date DATE, end_date DATE)
BEGIN
DECLARE crt_date DATE;
SET crt_date=start_date;
WHILE crt_date < end_date DO
INSERT INTO calendar VALUES(crt_date);
SET crt_date = ADDDATE(crt_date, INTERVAL 1 DAY);
END WHILE;
END |
DELIMITER ;
CALL fill_calendar('2008-01-01', '2011-12-31');
答案 0 :(得分:3)
你会发现很难得到你想要的结果,除非你在每天为每种货币输入一个虚拟订单(这可以在fill_calendar程序中轻松完成以进行测试)。
目前,您想要的是使用公共链接加入日历,订单和货币;但是没有这样的链接(你有从日历到订单和订单到货币的链接,但从日历到货币都没有)。
如果你创建了这些虚拟订单,那么你就不需要改变架构了;数据本身将提供所需的链接。否则,您可能需要稍微更改架构。
答案 1 :(得分:2)
我发布这个作为答案,因为评论可能会非常大。 感谢Mark指出我正确的方向。 Mark的答案有效,但意味着对日历表的架构更改,我并不热衷于此,因为我可能需要将来更灵活的报告(例如, product_id )
这有效 - 但它可能不优雅。我会将这个问题留下“未答复”几天,以确定是否有人能提出更好的解决方案。
其他架构和数据(添加产品表):
CREATE TABLE products
(
id INT PRIMARY KEY,
name VARCHAR(255)
);
INSERT INTO products (id, name)
VALUES
(1, 'Widget'),
(2, 'Midget'),
(3, 'Gidget');
现在使用此查询我得到了我想要的答案:
SELECT cal.date AS date, currency.name AS currency, products.name AS product,
IFNULL(SUM(orders.order_value),0) AS total_value
FROM orders
RIGHT JOIN
(
SELECT cal.datefield AS date, cur.id AS currency, prod.id AS product
FROM calendar cal
CROSS JOIN currency cur
CROSS JOIN products prod
) cal
ON (DATE(orders.order_date) = cal.date)
AND orders.currency_id = cal.currency
AND orders.product_id = cal.product
JOIN currency ON cal.currency = currency.id
JOIN products ON cal.product = products.id
WHERE (cal.date BETWEEN (SELECT MIN(DATE(order_date)) FROM orders) AND (SELECT MAX(DATE(order_date)) FROM orders))
GROUP BY date, cal.currency,cal.product
这为我提供了所有日期的所有数据点,如果它们不存在则为零。
+------------+-----------+--------+-------------+
| date | currency | product| total_value |
+------------+-----------+--------+-------------+
| 2009-08-15 | Euro | Widget | 40.97 |
| 2009-08-15 | Euro | Midget | 40.97 |
| 2009-08-15 | Euro | Gidget | 0.00 |
| 2009-08-15 | US Dollar | Widget | 12.50 |
| 2009-08-15 | US Dollar | Midget | 12.50 |
| 2009-08-15 | US Dollar | Gidget | 0.00 |
| 2009-08-15 | CA Dollar | Widget | 122.60 |
| 2009-08-15 | CA Dollar | Midget | 0.00 |
| 2009-08-15 | CA Dollar | Gidget | 0.00 |
| 2009-08-16 | Euro | Widget | 0.00 |
| 2009-08-16 | Euro | Midget | 0.00 |
| 2009-08-16 | Euro | Gidget | 0.00 |
| 2009-08-16 | US Dollar | Widget | 0.00 |
| 2009-08-16 | US Dollar | Midget | 0.00 |
| 2009-08-16 | US Dollar | Gidget | 0.00 |
| 2009-08-16 | CA Dollar | Widget | 0.00 |
| 2009-08-16 | CA Dollar | Midget | 0.00 |
| 2009-08-16 | CA Dollar | Gidget | 0.00 |
| 2009-08-17 | Euro | Widget | 0.00 |
| 2009-08-17 | Euro | Midget | 0.00 |
| 2009-08-17 | Euro | Gidget | 0.00 |
| 2009-08-17 | US Dollar | Widget | 12.50 |
| 2009-08-17 | US Dollar | Midget | 12.50 |
| 2009-08-17 | US Dollar | Gidget | 0.00 |
| 2009-08-17 | CA Dollar | Widget | 0.00 |
| 2009-08-17 | CA Dollar | Midget | 122.60 |
| 2009-08-17 | CA Dollar | Gidget | 0.00 |
| 2009-08-18 | Euro | Widget | 40.97 |
| 2009-08-18 | Euro | Midget | 40.97 |
| 2009-08-18 | Euro | Gidget | 0.00 |
| 2009-08-18 | US Dollar | Widget | 0.00 |
| 2009-08-18 | US Dollar | Midget | 0.00 |
| 2009-08-18 | US Dollar | Gidget | 0.00 |
| 2009-08-18 | CA Dollar | Widget | 122.60 |
| 2009-08-18 | CA Dollar | Midget | 122.60 |
| 2009-08-18 | CA Dollar | Gidget | 0.00 |
+------------+-----------+--------+-------------+
这在子查询上使用了一个JOIN,我觉得它不是很高效,但它适用于这个小数据集 - 我会生成更多的数据,看看它是怎么回事。