选择N个月前某一天创建的所有记录

时间:2018-01-03 11:13:13

标签: mysql sql

给出一个表orders

+-----+---------+-------------------------+
| id  |  price  |         created_at      |
+-----+---------+-------------------------+
|  1  |  16.50  | 2017-02-28 12:52:00.824 |
|  2  |  22.00  | 2017-10-03 15:12:39.107 |
|  3  |  50.00  | 2017-12-03 12:54:42.658 |
|  4  |  12.00  | 2018-01-02 07:21:47.808 |
|  .  |   .     |             .           |
|  .  |   .     |             .           |
|  .  |   .     |             .           |
+-----+---------+-------------------------+

和当前日期:

+---------------------+
| NOW()               |
+---------------------+
| 2018-01-03 10:33:14 |
+---------------------+

我想选择几个月前当天创建的所有记录。因此,对于上述数据,我的查询应返回:

+-----+---------+-------------------------+
| id  |  price  |         created_at      |
+-----+---------+-------------------------+
|  2  |  22.00  | 2017-10-03 15:12:39.107 |
|  3  |  50.00  | 2017-12-03 12:54:42.658 |
+-----+---------+-------------------------+

但是在一个月的最后一天有一些边缘情况

  • 如果是31个月,那就是琐碎的
  • 如果是30个月,查询应返回每月30日和31日创建的所有记录
  • 如果它是2月的闰年,则查询应返回在每月的29日,30日和31日创建的所有记录。
  • 如果它是正常年份的2月份,则查询应返回在月份的28日,29日,30日和31日创建的所有记录。

我尝试的是这样的:

SELECT * FROM orders
JOIN (
  SELECT id, PERIOD_DIFF(
    DATE_FORMAT(NOW(), "%Y%m"),
    DATE_FORMAT(created_at, "%Y%m")
  ) AS diff
  FROM orders
) AS periods
ON orders.id = periods.id
WHERE DATEDIFF(created_at + INTERVAL diff MONTH, NOW()) = 0;

但它并没有覆盖边缘情况,我相信有一种更聪明的方式(可能没有子查询)来实现预期的结果。

<小时/> 修改 为了给你更多的背景 - 我需要的是一种循环。我有一个cron job计划每天午夜运行一次。此作业应选择几个月前当天创建的ids orders,然后刷新与ids相关的其他一些数据。重要的是每月刷新一次这个数据 - 这就是为什么几个月的最后一天如此重要。

例如,给定以下创建日期:

DATES = [
  2015-05-30, 2016-02-29, 2016-10-03,
  2016-12-31, 2017-05-28, 2018-01-03
]

+---------------+------------------------------------+
|     NOW()     |         SHOULD BE INCLUDED         |
+---------------+------------------------------------+
|  2018-01-03   | 2016-10-03, 2018-01-03             |
|  2018-02-28   | 2016-02-29, 2016-12-31, 2017-05-28 |
|  2018-04-30   | 2015-05-30, 2016-12-31             |
|  2018-10-31   | 2016-12-31                         |
+---------------+------------------------------------+

3 个答案:

答案 0 :(得分:1)

使用DAYOFMONTH()函数比较NOW()和created_at的日期。 像这样:

SELECT * FROM ORDERS
WHERE (DAYOFMONTH(NOW() < LAST_DAY(NOW()) -- if not last day of month 
        AND DAYOFMONTH(created_at) = DAYOFMONTH(NOW())
OR (LAST_DAY(NOW()) = DAYOFMONTH(NOW()) -- if last day of month
     AND DAYOFMONTH(NOW()) BETWEEN DAYOFMONTH(created_at) AND LAST_DAY(created_at)) -- 

答案 1 :(得分:1)

我可以建议稍微简化Walerian吗?

SELECT 
    *

FROM 
    orders

WHERE 
    (
        DAYOFMONTH(created_at) = DAYOFMONTH( NOW() ) --monthdays that match
    )
    OR
    (
        (  DAYOFMONTH(  LAST_DAY( NOW() )  ) = DAYOFMONTH( NOW() )  ) --base date is end of month
        AND
        (  DAYOFMONTH(created_at) > DAYOFMONTH( NOW() )  ) --target monthdays are beyond base monthday
    )

顺便说一下,我没有MySQL环境,所以我只是相信这些是MySQL中正确的功能。

答案 2 :(得分:0)

受到suresubs's回答的启发,我弄清楚查询应该是什么样子。

SELECT * FROM orders
-- (1)
WHERE (DAYOFMONTH(NOW) < DAYOFMONTH(LAST_DAY(NOW()))
  AND DAYOFMONTH(created_at) = DAYOFMONTH(NOW()))
-- (2)
OR (DAYOFMONTH(LAST_DAY(NOW())) = DAYOFMONTH(NOW())
  AND DAYOFMONTH(created_at) BETWEEN DAYOFMONTH(NOW()) AND DAYOFMONTH(LAST_DAY(created_at)));

它使用DAYOFMONTH()LAST_DAY()函数,它分为两种情况:

  1. 今天的最后一天
  2. 今天是本月的最后一天。