查询未在日期范围内查找最后一天的行(使用< =)?

时间:2012-07-20 19:21:18

标签: mysql grails

在我非常简单的查询中查找日期范围的Deposit对象:

    String sqlQuery ="select d from Deposit d where status in('PENDING', 'SKIPPED') and  d.depositDate <= '${endDateString}'"

    def allUnprocessedDeposits = Deposit.executeQuery(sqlQuery)

不返回depositDate等于endDateString的行。 (?!?)例如,如果我将所有d.depositDate行更新为相同的日期,并将该日期作为endDateString提供,则不返回任何行。

使用grails 2.0.3和MySql 5.1 ......

感谢任何有答案的人。这看起来非常简单,但却令人烦恼。

3 个答案:

答案 0 :(得分:3)

当你只给一天DateTime字段时,mySQL会假设午夜,所以在午夜之后有时间的任何内容都不会包含在你的结果中。你最好说约会&lt; [结束日期+ 1]

答案 1 :(得分:2)

顺便说一句,你应该使用带参数的查询,它更安全:

String sqlQuery ="select d from Deposit d where status in('PENDING', 'SKIPPED') and  d.depositDate <= :endDate"

def allUnprocessedDeposits = Deposit.executeQuery(sqlQuery,[endDate:endDateVariable])

答案 2 :(得分:2)

根据您提供的信息,最可能的解释是depositDate列被定义为DATETIME或TIMESTAMP。请注意,除日期外,这些数据类型还存储时间值(分辨率低至一秒)。 e.g。

'2012-07-20 11:35:46'

比较DATETIME值所以是DATE文字(没有时间成分),例如

'2012-07-20 11:35:46' <= '2012-07-20'

相当于与DATETIME文字进行比较,时间值为午夜:

'2012-07-20 11:35:46' <= '2012-07-20 00:00:00'

显然会返回FALSE。这就是为什么你的查询没有返回你期望的行的最可能的解释。


建议修复:

对于DATETIME和TIMESTAMP列的谓词,获得“日”的正常模式是从给定日期的午夜到第二天的午夜进行范围扫描。我们想要检查的是该列是否比第二天的午夜少:

'2012-07-20 11:35:46' < DATE_ADD('2012-07-20', INTERVAL 1 DAY)

这相当于比较21日午夜。

'2012-07-20 11:35:46' < '2012-07-21 00:00:00'

在您的情况下,由于您已经有“结束日期”值,因此最简单的更改是简单地更改查询文本。

只需将一天添加到您传入的日期值,然后将比较运算符从<=更改为<。 (我假设你只提供日期部分,并允许时间部分默认为午夜。)

... d.depositDate < DATE_ADD('${endDateString}',INTERVAL 1 DAY)"

我们更喜欢这种模式,因为它在DATEDATETIMETIMESTAMP上同样有效。

(注意:我们更喜欢这种模式的另一个原因是它使用“时间点”值甚至更精细的分辨率(例如Microsoft SQL Server DATETIME,其精度低至3毫秒,并且检查&lt; = 23:59.59结果不足。)

您的代码可以确保参数值只是日期,或者是时间组件为午夜的日期,但是通过将参数包装在CAST( AS DATE) <中,可以很容易地让SQL查询执行此操作/ p>

注意:您希望避免将列引用d.depositDate包装在任何函数中,因为这将禁用MySQL执行索引范围扫描操作的能力(以满足谓词)。


注意:所有关于SQL注入漏洞的常见警告都适用于此处,如果参数值由用户提供,您希望使用绑定参数或转义提供的....值

考虑当恶意用户提供如下值时会发生什么:

2012-07-20'; DELETE FROM Deposit ; SELECT '1

考虑将哪些语句传递给数据库。有几种方法可以解决这个问题,以阻止这种攻击。