在我非常简单的查询中查找日期范围的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 ......
感谢任何有答案的人。这看起来非常简单,但却令人烦恼。
答案 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)"
我们更喜欢这种模式,因为它在DATE
,DATETIME
和TIMESTAMP
上同样有效。
(注意:我们更喜欢这种模式的另一个原因是它使用“时间点”值甚至更精细的分辨率(例如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
考虑将哪些语句传递给数据库。有几种方法可以解决这个问题,以阻止这种攻击。