SQL - DATEDIFF 和 WHERE/HAVING 的聚合函数

SQL - Aggregate Function for DATEDIFF and WHERE/HAVING

我正在尝试查询数据库以查找与我传入的参数最接近的日期。

我有一个问题,我一直在 IRC 和列表中处理一段时间,但找不到解决方案。我正在使用 python DBAPI (sqlalchemy) 并通过 CLI 和 Flask 应用程序访问数据(但这不应该影响 SQL)。数据库 MySQL 带有 InnoDB 引擎。

查询将取决于名为 target_expiration 的因素,但数据库中通常不存在该因素的值。我需要找到最接近的值。例如,我将传递 target_date='2015-06-02' 并希望它对我来说 return 2015-06-03 结果和 2015-06-01 距离我 [=18] 一天的结果=].

尝试解决方案:

SELECT * FROM table1 HAVING MIN(ABS(DATEDIFF(table1.Expiration, 
'2015-06-02'))) = ABS(DATEDIFF(table1.Expiration, '2015-06-02'));

这 return 没有行。我可以 运行 相同的 MIN(ABS(DATEDIFF(...))) 到 return a scalar() 它成功并打印一个值。

我尝试使用 HAVING 函数来使用聚合函数。我正在寻找扫描数据库并找出离我的目标日期最近的到期日期。如果它为零,我就完成了 - 我找到了日期,然后我只想 return 所有匹配的日期。如果是一天之后,我不知道是向前一天还是向后一天(我将处理添加更多功能以供稍后选择,但现在我只想 return 所有值向前或向后 n 天)。

示例数据: table1

这是一个极其有限的数据样本,实际上我有大约 40 列和数十亿行,所以速度是一个因素。

+------------+-------------+-------+
| Expiration | ProductType | Price |
+------------+-------------+-------+
| 2015-06-01 |      2      |   25  |
+------------+-------------+-------+
| 2015-06-03 |      1      |   22  |
+------------+-------------+-------+
| 2015-05-28 |      1      |   22  |
+------------+-------------+-------+
| 2015-05-28 |      2      |   28  |
+------------+-------------+-------+
| 2015-05-28 |      1      |   22  |
+------------+-------------+-------+
| 2015-06-04 |      2      |   28  |
+------------+-------------+-------+
| 2015-05-25 |      1      |   22  |
+------------+-------------+-------+
| 2015-05-25 |      2      |   28  |
+------------+-------------+-------+

单个结果:

SELECT * 
FROM table1 
ORDER BY ABS(DATEDIFF(table1.Expiration, '2015-06-02')) ASC
LIMIT 1;

如果您担心 "tie":

SELECT * 
FROM table1 
WHERE ABS(DATEDIFF(table1.Expiration, '2015-06-02')) = (
   SELECT MIN(ABS(DATEDIFF(table1.Expiration, '2015-06-02')))
   FROM table1
);

但是请注意,这些查询永远不会很快;第二个要求 table 中的每一行都被检查两次,并且在这两个函数中使用都阻止索引提供任何帮助。

简单如何

SELECT  *
    FROM  table1
    ORDER BY ABS(DATEDIFF(Expiration, '2015-06-02'));

或者,如果 table 很大,并且您有 INDEX(Expiration),这应该更快:

( SELECT  *, 
        ABS(DATEDIFF(Expiration, '2015-06-02')) AS diff
    FROM  table1
    WHERE Expiration >= '2015-06-02'
    ORDER BY Expiration ASC LIMIT 1 )
UNION ALL
( SELECT  *, 
        ABS(DATEDIFF(Expiration, '2015-06-02')) AS diff
    FROM  table1
    WHERE Expiration < '2015-06-02'
    ORDER BY Expiration DESC LIMIT 1 )
ORDER BY diff 
LIMIT 1;