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

时间:2015-05-05 22:11:34

标签: python mysql innodb mysql-python

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

我遇到了一个问题,我已经在IRC和列表中工作了一段时间,但无法找到解决方案。我使用python DBAPI(sqlalchemy)并通过CLI和Flask App访问数据(但这不应该影响SQL)。该数据库是带有InnoDB引擎的MySQL。

查询将取决于名为target_expiration的因子,但该值通常不存在于DB中。我需要找到哪个值最接近。例如,我会传递target_date='2015-06-02'并希望它返回给我2015-06-03结果和距我2015-06-01一天的target_date结果。

尝试解决方案

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

这不返回任何行。我可以运行相同的MIN(ABS(DATEDIFF(...)))来返回scalar()并成功并打印一个值。

我尝试使用HAVING函数来使用聚合函数。我正在寻找扫描数据库并找出我最接近目标日期的截止日期。如果它为零,我已经完成了 - 我找到了日期,然后我只想返回所有匹配的日期。如果它是一天之后,我不知道它是向前还是向后退一天(我将处理添加更多功能以便稍后选择,但是现在我和#39; d只想返回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  |
+------------+-------------+-------+

2 个答案:

答案 0 :(得分:1)

单个结果:

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
);

但请注意,这些查询永远不会很快;第二行要求对表中的每一行进行两次检查,并且在两种函数用法中都要求索引不提供任何帮助。

答案 1 :(得分:0)

如何简单地

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

或者,如果表格很大,并且您有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;