对于我的网站,我有一个忠诚度计划,如果客户在过去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())
一起使用,以与问题的前几位保持一致。
答案 0 :(得分:4)
以下基本上是如何做你想要的。请注意,对“30天”的引用是粗略估计,您可能要寻找的是“29天”或“31天”,以便获得您想要的确切日期。
检索仍然有效的日期和金额列表,即在过去30天内(如您在示例中所做的那样),作为表格(我称之为有效)就像你展示的那个。
将新表格(有效)加入原始表格,其中活动中的一行使用<加入原始表格的所有行em> date 字段。计算原始表中的总金额。新表格中包含有效的日期字段和 Totol 字段,该字段是原始联合记录中所有金额的总和表
从结果表中选择金额大于100.00的所有记录,并创建一个新表格,其中日期和这些记录的最小金额。< / p>
从这些日期开始提前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;
现在,使用上述查询
创建 VIEWCREATE 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)
您需要采取以下步骤(每位用户):
这是一个查询:
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来检查查询是否正常工作。
此外,这不是编写此代码的最佳方式,但我试图通过使用子查询尽可能保持安全,因为我没有数据来测试它。如果您遇到任何错误,请告诉我。