Mysql 更改数据库模式或查询以返回给定时间段内的余额

Mysql 更改数据库模式或查询以返回给定时间段内的余额,mysql,sql,Mysql,Sql,我提出了以下模式: 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, provide

我提出了以下模式:

  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
我需要在模式或查询中更改什么才能以这种方式返回结果

欢迎任何提示。提前感谢

在选择列表中包含创建日期,并在每个查询中包含GROUP BY子句

放弃用于联接操作的老式逗号运算符,并将其替换为左联接

要返回没有订单、没有付款或没有发票的日期,我们需要一个保证返回日期值的单独行源。例如,我们可以使用内联视图:

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
内联视图只是一个选项。如果我们有一个日历表,其中包含我们感兴趣的三个日期的行,那么我们可以使用它。重要的是,我们有一个查询,它保证返回到我们想要返回的具有不同created_at date值的整整三行

一旦我们有了它,我们就可以添加一个左连接来获取该日期的账单价值

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
为了每月获得结果,我们需要一个每月返回一行的日历查询。让我们假设我们将返回一个日期值,该值作为一个月的第一天。例如:

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
内联视图查询需要按创建的月份分组,因此它们为每个月份值返回一个值。我的首选是使用DATE_FORMAT函数返回月份的第一天,该天是从created_at派生的。但还有其他方法可以做到这一点。目标是为“2017-02-01”返回单行,为“2017-03-01”返回单行。请注意,创建时的日期范围从“2017-02-01”延伸至“2017-04-01”,但不包括“2017-04-01”,因此我们得到了整个月的总数

           ( SELECT DATE_FORMAT(bills.created_at,'%Y-%m-01') AS created_month
                  , SUM(bills.price)                         AS  bills
               FROM bills
              WHERE bills.created_at >= '2017-02-01'
                AND bills.created_at <  '2017-03-01' + INTERVAL 1 MONTH
              GROUP BY DATE_FORMAT(bills.created_at,'%Y-%m-01')
           ) b

哇!感谢@spencer7593提供了极其详细的答案!只是好奇:这张日历表只能放日期?如果是这样的话,它将需要通过一个预定的过程填充每个未来的日期。这是一种常见的做法吗?我不知道有日历表是否常见。对于其他数据库,很容易生成日期列表、Oracle recursive CONNECT BY inline view、SQL Server recursive CTE等。MySQL中没有简单的工具,因此我们创建了一个表cal dt DATE NOT NULL PRIMARY KEY ENGINE=InnoDB,并插入了满足我们要求的连续行范围。2001年1月1日至2037年12月31日。
           ( SELECT DATE_FORMAT(bills.created_at,'%Y-%m-01') AS created_month
                  , SUM(bills.price)                         AS  bills
               FROM bills
              WHERE bills.created_at >= '2017-02-01'
                AND bills.created_at <  '2017-03-01' + INTERVAL 1 MONTH
              GROUP BY DATE_FORMAT(bills.created_at,'%Y-%m-01')
           ) b