查找30天内的条件已过去的日期

时间:2016-03-16 04:10:47

标签: mysql sql date

对于我的网站,我有一个忠诚度计划,如果客户在过去30天内花了100美元,他们会获得一些好处。如下查询:

SELECT u.username, SUM(total-shipcost) as tot
    FROM orders o
        LEFT JOIN users u
        ON u.userident = o.user
    WHERE shipped = 1
    AND user = :user
    AND date >= DATE(NOW() - INTERVAL 30 DAY)

:user是他们的用户ID。该结果的第2列给出了客户在过去30天内花了多少钱,如果超过100天,则获得奖金。

我想向用户显示他们将在哪一天离开忠诚度计划。像奖金到期之前的#34; x天,",但我该怎么做?

今天的日期,3月16日,以及用户的订单记录:

id | tot  |    date
-----------------------
84    38     2016-03-05
76    21     2016-02-29
74    49     2016-02-20
61    42     2015-12-28

此用户现在是忠诚度计划的一部分,但在3月20日离开。我可以做什么SQL返回用户留在忠诚度计划上的天数(4)?

如果用户随后下了另一个订单:

id | tot  |    date
-----------------------
87    12     2016-03-09

他们仍然在忠诚度计划中直到20日,所以剩下的日子在这种情况下并没有改变,但如果总数是50,那么他们将在29日离开程序(所以而不是4天,剩下13天)。对于它的价值,我只关心当前日期前30天。没有考虑需要28,29,31天的月份。

一些create table代码:

create table users (
    userident int,
    username varchar(100)
);

insert into users values 
    (1, 'Bob');

create table orders (
    id int,
    user int,
    shipped int,
    date date,
    total decimal(6,2),
    shipcost decimal(3,2)
);

insert into orders values
    (84, 1, 1, '2016-03-05', 40.50, 2.50),
    (76, 1, 1, '2016-02-29', 22.00, 1.00),
    (74, 1, 1, '2016-02-20', 56.31, 7.31),
    (61, 1, 1, '2015-12-28', 43.10, 1.10);

我正在寻找的一个示例输出是:

userident | username | days_left
--------------------------------
    1          Bob        4

今天使用3月16日与DATE(NOW())一起使用,以与问题的前几位保持一致。

4 个答案:

答案 0 :(得分:4)

以下基本上是如何做你想要的。请注意,对“30天”的引用是粗略估计,您可能要寻找的是“29天”或“31天”,以便获得您想要的确切日期。

  1. 检索仍然有效的日期和金额列表,即在过去30天内(如您在示例中所做的那样),作为表格(我称之为有效)就像你展示的那个。

  2. 将新表格(有效)加入原始表格,其中活动中的一行使用<加入原始表格的所有行em> date 字段。计算原始表中的总金额。新表格中包含有效日期字段和 Totol 字段,该字段是原始联合记录中所有金额的总和表

  3. 从结果表中选择金额大于100.00的所有记录,并创建一个新表格,其中日期和这些记录的最小金额。< / p>

  4. 从这些日期开始提前30天计算,以查找其忠诚度计划的结束日期。

答案 1 :(得分:3)

简单解决方案

看到你是如何进行第一次查询的,我猜想当你到达&#34;过期日期&#34;时,你已经知道用户在过去30天内达到了100分。然后你可以这样做:

SELECT DATE_ADD(MIN(date),INTERVAL 30 DAY) 
FROM orders o
WHERE shipped = 1
AND user = :user
AND date >= (DATE(NOW() - INTERVAL 30 DAY)) 

它需要用户在过去30天内的最短订单日期,并在结果中添加30天。

但这实际上是一个很糟糕的设计来实现你想要的。 你最好进一步思考并实施下一步。

高级解决方案

为了重现以下所有解决方案,我使用了Trincot亲切构建的小提琴,并将其扩展为测试更多数据:4个用户有4个订单。

SQL FIddle http://sqlfiddle.com/#!9/668939/1

第1步:设计

以下查询将返回符合忠诚度计划条件的所有用户,以及30天内的早期订单日期以及从较早日期计算的忠诚度计划到期日期以及到期前的天数。

SELECT O.user, u.username, SUM(total-shipcost) as tot, MIN(date) AS mindate, 
       DATE_ADD(MIN(date),INTERVAL 30 DAY) AS expirationdate, 
       DATEDIFF(DATE_ADD(MIN(date),INTERVAL 30 DAY), DATE(NOW())) AS daysleft
FROM orders o      
    LEFT JOIN users u
    ON u.userident = o.user                         
WHERE shipped = 1                                              
AND date >= DATE(NOW() - INTERVAL 30 DAY)
GROUP BY user
HAVING tot >= 100;

现在,使用上述查询

创建 VIEW
CREATE VIEW loyalty_program AS
    SELECT O.user, u.username, SUM(total-shipcost) as tot, MIN(date) AS mindate, 
           DATE_ADD(MIN(date),INTERVAL 30 DAY) AS expirationdate, 
           DATEDIFF(DATE_ADD(MIN(date),INTERVAL 30 DAY), DATE(NOW())) AS daysleft
    FROM orders o      
        LEFT JOIN users u
        ON u.userident = o.user                         
    WHERE shipped = 1                                              
    AND date >= DATE(NOW() - INTERVAL 30 DAY)
    GROUP BY user
    HAVING tot >= 100;

了解这只是对您的数据库的一次性操作非常重要。

第2步:使用新的视图

获得视图后,您可以轻松地为所有用户轻松获得状态&#34;忠诚度计划:

SELECT * FROM loyalty_program

user    username    tot mindate                 expirationdate  daysleft
1           John    153 February, 28 2016       March, 29 2016  9
2           Joe     112 February, 24 2016       March, 25 2016  5
3           Jack    474 February, 23 2016       March, 24 2016  4
4           Averel  115 February, 22 2016       March, 23 2016  3

对于特定用户,您可以像这样获得您要查找的日期:

SELECT expirationdate FROM loyalty_program WHERE username='Joe'

您还可以请求所有过期日期为

的用户
SELECT user FROM loyalty_program WHERE expirationdate=DATE(NOW))

但是,在玩了你的视图后,你会发现其他简单的可能性。

结论

让您的生活更轻松:学习使用VIEWS!

答案 2 :(得分:3)

您需要采取以下步骤(每位用户):

  • 加入订单表,以计算过去30天内任何开始日期的不同(奖励)开始日期的总和
  • 仅从那些记录中选择产生100或更多总和的起始日期
  • 仅从那些记录中选择具有最近开始日期的记录:这是所选用户的奖励期间的开始。

这是一个查询:

SELECT      u.userident,
            u.username,
            MAX(base.date) AS bonus_start,
            DATE(MAX(base.date) + INTERVAL 30 DAY) AS bonus_expiry,
            30-DATEDIFF(NOW(), MAX(base.date)) AS bonus_days_left
FROM        users u
LEFT JOIN   (
                SELECT     o.user, 
                           first.date AS date,
                           SUM(o.total-o.shipcost) as tot
                FROM       orders first
                INNER JOIN orders o
                        ON o.user = first.user
                       AND o.shipped = 1
                       AND o.date >= first.date
                WHERE      first.shipped = 1
                       AND first.date >= DATE(NOW() - INTERVAL 30 DAY)
                GROUP BY   o.user,
                           first.date
                HAVING     SUM(o.total-o.shipcost) >= 100
            ) AS base
         ON base.user = u.userident
GROUP BY    u.username,
            u.userident 

这是fiddle

将此输入作为订单:

+----+------+---------+------------+-------+----------+
| id | user | shipped |    date    | total | shipcost |
+----+------+---------+------------+-------+----------+
| 61 |   1  |    1    | 2015-12-28 |   42  |    0     |
| 74 |   1  |    1    | 2016-02-20 |   49  |    0     |
| 76 |   1  |    1    | 2016-02-29 |   21  |    0     |
| 84 |   1  |    1    | 2016-03-05 |   38  |    0     |
| 87 |   1  |    1    | 2016-03-09 |   50  |    0     |
+----+------+---------+------------+-------+----------+

以上查询将返回此输出(在2016-03-20执行时):

+-----------+----------+-------------+--------------+-----------------+
| userident | username | bonus_start | bonus_expiry | bonus_days_left |
+-----------+----------+-------------+--------------+-----------------+
|       1   | John     | 2016-02-29  |  2016-03-30  |         10      |
+-----------+----------+-------------+--------------+-----------------+

答案 3 :(得分:2)

我假设你的桌子看起来像这样:

user | id | total |    date
-------------------------------
 12     84    38     2016-03-05
 12     76    21     2016-02-29
 23     74    49     2016-02-20
 23     61    42     2015-12-28

然后试试这个:

SELECT x.user, x.date, x.id, x.cum_sum, d,date, DATEDIFF(NOW(), x.date) from (SELECT a.user, a.id, a.date, a.total, 
  (SELECT SUM(b.total) FROM order_table b WHERE b.date <= a.date and a.user=b.user ORDER BY b.user, b.id DESC) AS cum_sum FROM order_table a where a.date>=DATE(NOW() - INTERVAL 30 DAY) ORDER BY a.user, a.id DESC) as x 
left join 
(SELECT c.user, c.date as start_date, c.id from (SELECT a.user, a.id, a.date, a.total, 
  (SELECT SUM(b.total) FROM order_table b WHERE b.date <= a.date and a.user=b.user ORDER BY b.user, b.id DESC) AS cum_sum FROM order_table a where a.date>=DATE(NOW() - INTERVAL 30 DAY) ORDER BY a.user, a.id DESC) as c WHERE FLOOR(c.cum_sum/100)=MIN(FLOOR(c.cum_sum/100)) and MOD(c.cum_sum,100)=MAX(MOD(c.cum_sum,100)) group by concat(c.user, "_", c.id)) as d on concat(x.user, "_", x.id)=concat(d.user, "_", d.id) where x.date=d.date;

你会得到一张这样的表:

user |   Date      |    cum_sum     |   start_date  |   Time_left
    ----------------------------------------------------
  12    2016-03-05         423          2016-03-05      24
  13    2016-02-29         525          2016-02-29      12
  23    2016-02-20         944          2016-02-20       3
  29    2015-12-28         154          2015-12-28       4

我没有测试过这个。但我想要做的是按照id和user的降序创建一个表,并获得累积的总列数。我使用此表创建了另一个表,其中包含累计总数,以及每个用户的相关日期(即计算日期差异的日期)。我已经离开了这两个表,并把条件放在x.date = d.date。我在表中放了start_date和date来检查查询是否正常工作。

此外,这不是编写此代码的最佳方式,但我试图通过使用子查询尽可能保持安全,因为我没有数据来测试它。如果您遇到任何错误,请告诉我。