MySQL查询根据可变持续时间条目生成每月输出

时间:2014-03-17 17:11:19

标签: mysql sql

好的,我有一张表格,其中包含与日期和持续时间相关的付款。

示例数据:

purchase_id  date_purchased         user_id   duration (in months)   amount_income
1            2013-12-28 00:00:00    1         2                      £15
2            2014-01-04 00:00:00    2         1                      £10
3            2014-02-04 00:00:00    3         6                      £40

*因此,用户支付的时间越长,他们每月支付的费用就越少。

我想要显示的是基于此表的每月总收入。因此,2个月内支付15英镑将意味着每月收入7.50英镑,为期2个月。

示例输出:

Month       Amount
2013-12     £7.50
2014-01     £17.50
2014-02     £6.66
2014-03     £6.66
2014-04     £6.66
2014-05     £6.66
2014-06     £6.66
2014-07     £6.66

此输出对应于上面的示例输入。希望它能澄清每月收入的细分。

如何在查询中执行此操作?

3 个答案:

答案 0 :(得分:0)

根据假设您可以轻松地从日期时间中提取月份,因此真正的问题是关于聚合逻辑,并且您可以创建数字表。

这是一个显示模式的简单示例。

sqlfiddle

CREATE TABLE Num (num int);
INSERT INTO Num VALUES (0),(1),(2),(3),(4);

CREATE TABLE Tbl (start int, run int);
INSERT INTO Tbl VALUES (1,2),(2,3);

SELECT start + num active_month
      ,count(*) * 10 income
  FROM Tbl
       INNER JOIN
       Num ON num < run
GROUP BY start + num

答案 1 :(得分:0)

怎么样:

SELECT a.my_date, a.income, IFNULL(SUM(DISTINCT(a.income)) + sum( b.income ), a.income) as roll_up
FROM (
SELECT purchase_id, DATE_FORMAT( date_purchased, '%y-%m') AS my_date, SUM( amount_income / duration ) AS "income"
FROM incomes
GROUP BY my_date
) AS a
LEFT OUTER JOIN (
SELECT purchase_id, DATE_FORMAT( date_purchased, '%y-%m') AS my_date, SUM(amount_income / duration ) AS "income"
FROM incomes
GROUP BY my_date
) AS b ON ( a.purchase_id > b.purchase_id )

GROUP BY a.purchase_id

一次性完成这项工作有点棘手 - 而且可能会有所改进 - 但这会产生以下结果:

my_date  income     roll_up
13-12    8.5000     8.5000
14-01   10.0000     18.5000
14-02   16.6667     35.1667

我的数据集是:

1   2013-12-28 00:00:00     1   2   15
2   2014-01-04 00:00:00     2   1   10
3   2014-02-04 00:00:00     3   6   40
4   2013-12-29 00:00:00     4   1   1
5   2014-02-28 00:00:00     5   2   20

答案 2 :(得分:0)

像卡尔一样,我非常确定这里需要一些数字表。我个人喜欢the approach given here,它定义了一个视图(好吧,其中几个)来生成数字,而不是必须实际存储一个充满数字的表。无论您使用表格还是视图,当您SELECT时,它只是看起来像这样:

 n
---
 0
 1
 2
 3
 …

通过它你可以构建一个这样的查询:

SELECT
  purchases.purchase_id,
  purchases.date_purchased,
  purchases.duration,
  -- generator_16 is our numbers table
  generator_16.n,
  -- Below we calculate the year and month (year_mon) in the following way:
  -- (1) Get the first day of the year, e.g. if date_purchased is 2012-12-28,
  --     this gives us 2012-01-01.
  -- (2) Get the month number, e.g. 12 for 2012-12-28) and add that many months
  --     to the first day of the year, which gives us the first day of the
  --     month, 2012-12-01.
  -- (3) Add "n" months, where "n" is the number we get from the numbers table,
  --     starting at 0.
  DATE_ADD(                                              -- (3)
    DATE_ADD(                                            -- (2)
      MAKEDATE( YEAR(purchases.date_purchased), 1 ),     -- (1)
      INTERVAL MONTH(purchases.date_purchased) - 1 MONTH -- (2)
    ),
    INTERVAL generator_16.n MONTH                        -- (3)
  ) AS year_mon,
  purchases.amount_income / purchases.duration AS amount
FROM purchases
-- The below JOIN means that if `purchases.duration` is 3, we get three rows
-- that have 0, 1, and 2 in the `n` column, which we use as the number of dates
-- to add in (3) above.
JOIN generator_16
ON generator_16.n BETWEEN 0 AND purchases.duration - 1
ORDER BY purchases.purchase_id, year_mon;

这给了我们这样的结果(SQL Fiddle):

purchase_id  date_purchased  duration  n      year_mon  amount
-----------  --------------  --------  -  ------------  ------
          1    2013-12-28 …         2  0  2013-12-01 …     7.5
          1    2013-12-28 …         2  1  2014-01-01 …     7.5

          2    2014-01-04 …         1  0  2014-01-01 …      10

          3    2014-02-04 …         6  0  2014-02-01 …  6.6667
          3    2014-02-04 …         6  1  2014-03-01 …  6.6667
          3    2014-02-04 …         6  2  2014-04-01 …  6.6667
          3    2014-02-04 …         6  3  2014-05-01 …  6.6667
          3    2014-02-04 …         6  4  2014-06-01 …  6.6667
          3    2014-02-04 …         6  5  2014-07-01 …  6.6667

我插入空行以分隔purchase_id群组,以便您了解群组中每个项目的n0增加到duration - 1的方式。如您所见,year_mon等于n个月加date_purchased个月的第一天n个月后,amount等于{amount_income / duration 1}}。

我们差不多完成了,但是你可以看到year_mon有重复:2014-01-01会显示两次。这是一个好消息,因为我们可以使用GROUP BY按该列进行分组,然后使用SUM(amount)获取该月的总数:

SELECT
  DATE_ADD(
    DATE_ADD(
      MAKEDATE( YEAR(purchases.date_purchased), 1 ),
      INTERVAL MONTH(purchases.date_purchased) - 1 MONTH
    ),
    INTERVAL generator_16.n MONTH
  ) AS year_mon,
  SUM(purchases.amount_income / purchases.duration) AS total
FROM purchases
JOIN generator_16
ON generator_16.n BETWEEN 0 AND purchases.duration - 1
GROUP BY year_mon
ORDER BY year_mon;

此查询与上个月之间的唯一区别是,我们会GROUP BY year_mon然后SUM(amount_income / duration)获取该月的total,从而产生此结果(SQL Fiddle) :

    year_mon   total
------------  ------
2013-12-01 …     7.5
2014-01-01 …    17.5
2014-02-01 …  6.6667
2014-03-01 …  6.6667
2014-04-01 …  6.6667
2014-05-01 …  6.6667
2014-06-01 …  6.6667
2014-07-01 …  6.6667

...当然,您可以使用DATE_FORMATCASTROUND来获取格式正确的日期和金额,或者您可以在前端代码中执行此操作。