如何使用开始和结束日期计算每个月的列中的数据数量

时间:2015-07-14 09:18:41

标签: mysql sql

我有一张包含相似信息的表格,我想在用户选择开始日期时提取这样的数据' 2015-01-22'和结束日期' 2015-07-31' 。 结果应如下所示。

Month        Total Quantity    
January: 8
February: 6
March: 0
April: 0
May: 2
June: 18
July: 6

这是一个示例查询和小提琴

CREATE TABLE orders
(
  id INT PRIMARY KEY AUTO_INCREMENT,
  order_date DATE,
  product_id INT,
  quantity INT,
  customer_id INT
);
INSERT INTO orders (order_date, product_id, quantity, customer_id)
  VALUES
  ('2015-01-01', 1, 2, 123),
  ('2015-01-06', 3, 6, 123),
  ('2015-02-14', 2, 4, 123),
  ('2015-02-15', 2, 2, 123),
  ('2015-05-16', 1, 1, 456),
  ('2015-05-17', 1, 1, 456),
  ('2015-06-18', 1, 5, 789),
  ('2015-06-18', 3, 7, 123),
  ('2015-06-10', 3, 6, 123),
  ('2015-07-13', 1, 5, 456),
  ('2015-07-14', 1, 1, 456);

http://sqlfiddle.com/#!2/01ac19/1

结果应该是每月订单总数

2 个答案:

答案 0 :(得分:1)

首先,你想要什么 NEED被称为"日历表"。它们是您可以制作的最有用的分析表。他们的个人定义和数据填写方式各不相同,并且不在此处讨论,但出于我们的目的,我们将使用以下最低定义:

CREATE TABLE Calendar (calendarDate DATE PRIMARY KEY,
                       year INTEGER,
                       month INTEGER
                       dayOfMonth INTEGER);

......并且它充满了您期望的数据(从您的业务开始时插入每个日期,到将来的合理点)。您还需要索引 - 批次索引。

接下来,您需要考虑一些关于数据库的重要事项:如果将函数输出用作标准,它们就无法使用索引。基本上,如果它不在SELECT子句中,使用函数(甚至通过一些隐式转换)会使查询变慢。因此,应该避免像YEAR(order_date)这样的事情 那么我们如何通过年或月等事物进行汇总?通过范围查询。如果数据库有一个索引,那么查找一个范围的起点和终点(并且可以很好地并行化)也相当便宜。在我们的示例中,范围为>= startOfMonth< startOfNextMonth。我们现在可以构建一个进程范围表:

SELECT year, month, 
       calendarDate AS monthStart, 
       calendarDate + INTERVAL 1 MONTH AS nextMonthStart
FROM Calendar
WHERE dayOfMonth = 1
      AND calendarDate >= :queryStartRange
      AND calendarDate < :queryEndRange

...其中:表示月初值,留作读者的练习。

现在,请记住我的说法&#34;没有功能&#34;? calendarDate + INTERVAL 1 MONTH实际上很重要。但是,这并不重要;结果表是如此之小(每年只有12行!),好的RDBMS可以将内容放在内存中以获得更快的结果(因为只需要更长的时间来命中索引)。

现在我们有了范围查询表,我们可以将它加入Orders(&#34; fact&#34;)表;

SELECT DRange.year, DRange.month, SUM(Orders.quantity) AS total_quantity
FROM (SELECT year, month, 
             calendarDate AS monthStart, 
             calendarDate + INTERVAL 1 MONTH AS nextMonthStart
      FROM Calendar
      WHERE dayOfMonth = 1
            AND calendarDate >= :queryStartRange
            AND calendarDate < :queryEndRange) AS DRange
JOIN Orders
  ON Orders.order_date >= DRange.monthStart
     AND Orders.order_date < DRange.nextMonthStart
GROUP BY DRange.year, DRange.month
ORDER BY DRange.year, DRange.month

Example Fiddle
(有趣的诀窍:如果一个月没有订单,则使用LEFT JOIN代替JOIN会为空数量行提供净值 - 例如您的示例数据中的3月和4月)

那么这对我们有什么影响?基础数据的范围查询访问,这将使查询更快。如果由于某种原因,order_date变为时间戳,则查询完全安全 - 我们将正确获取所有订单,并将其置于适当的月份。

答案 1 :(得分:0)

尝试此查询

SELECT MONTHNAME(DATE(order_date)) AS dateinfo, SUM(quantity) AS total_sales
FROM orders
GROUP BY dateinfo