In my very simple query to find Deposit objects for a date range:
String sqlQuery ="select d from Deposit d where status in('PENDING', 'SKIPPED') and d.depositDate <= '${endDateString}'"
def allUnprocessedDeposits = Deposit.executeQuery(sqlQuery)
no rows whose depositDate is equal to endDateString are returned. (?!?) For example, if I update all d.depositDate rows to be the the same date, and provide that date as the endDateString, no rows are returned.
Using grails 2.0.3 and MySql 5.1…
Thanks to anyone with the answer. This seems so very simple, yet annoyingly fails.
Given the information you’ve provided, the most likely explanation is that
depositDatecolumn is defined as DATETIME or TIMESTAMP. Note that these datatypes store the time value (in resolution down to a second) in addition to the date. e.g.A compare of a DATETIME value So a a DATE literal (without time component), e.g.
is equivalent to a compare with a DATETIME literal with a time value of midnight:
Which is obviously going to return FALSE. That’s the most likely explanation of why your query isn’t returning rows you expect it to.
Suggested Fix:
For predicates on DATETIME and TIMESTAMP columns, the normal pattern to get a “day” is to do a range scan from midnight of a given day up to midnight of the following day. What we want to check is whether the column is LESS THAN midnight of the following day:
That’s equivalent to comparing to midnight of the 21st.
In your case, since you already have an “end date” value, the easiest change for is to simply change the query text.
Just add one day to the date value you pass in, and change the comparison operator from
<=to<. (I’m assuming here that you are supplying only the date portion, and allowing the time portion to default to midnight.)We prefer this pattern because it works equally well on
DATE,DATETIMEandTIMESTAMP.(NOTE: Another reason we prefer this pattern is because it works with “point in time” values with even finer resolutions (e.g. Microsoft SQL Server DATETIME, which has a precision down to 3 ms, and doing a check of <= 23:59.59 winds up being insufficient.)
Your code can ensure that the argument value is the date only, or is a date with a time component of midnight, but it’s easy to have the SQL query do this, by wrapping your argument in a a
CAST( AS DATE)NOTE: you want to avoid wrapping the column reference
d.depositDateinside any function, because that will disable MySQL’s ability to perform an index range scan operation (to satisfy the predicate).NOTE: all the usual warnings about SQL injection vulnerabilities apply here, if the argument value is supplied by the user, you want to either use bind parameters or escape the supplied …. values
Consider what will happen when a malicious user supplies a value like:
Consider what statement(s) will get passed to the database. There are several approaches to dealing with this, to thwart such attacks.