Reputation: 474
Trying to search in a database a date range. Problem is, I cannot use the datetime column type in my database. To compensate, date's are displayed as three columns. a Month column, a Day column, and a Year column. Here is my SQL query:
SELECT COUNT(*)
FROM `import`
WHERE `call_day` BETWEEN 29 AND 15
AND `call_month` BETWEEN 9 AND 10
AND `call_year` BETWEEN 2013 AND 2013
You can see where I run into trouble. call_day needs to search between the 29th day and the 15th day. This won't work because 15 is smaller than 29, but I need it to work because the month is in the future :)
Any thoughts/solutions? No I cannot change the database in any way. Read only.
Upvotes: 4
Views: 212
Reputation: 92795
Besides the concatenation approach, which can be implemented in quite a few ways, e.g.
SELECT *
FROM import
WHERE STR_TO_DATE(CONCAT_WS('-', call_year, call_month, call_day), '%Y-%c-%e')
BETWEEN '2013-09-29' AND '2013-10-15'
or
SELECT *
FROM import
WHERE CONCAT(call_year, LPAD(call_month, 2, '0'), LPAD(call_day, 2, '0'))
BETWEEN '20130929' AND '20131015'
Here is SQLFiddle demo
that will always cause a full scan and assuming that date ranges in your queries usually don't span more than a few months you can also do
SELECT *
FROM import
WHERE (call_year = 2013 AND
call_month = 9 AND
call_day BETWEEN 29 AND DAY(LAST_DAY('2013-09-01'))) -- or just 30
OR (call_year = 2013 AND
call_month = 10 AND
call_day BETWEEN 1 AND 15)
Here is SQLFiddle demo
For a query that spans a year (e.g. from 2012-08-20
to 2013-10-15
)
SELECT *
FROM import
WHERE (call_year = 2012 AND
call_month = 8 AND
call_day BETWEEN 20 AND 31)
OR (call_year = 2012 AND
call_month BETWEEN 9 AND 12 AND
call_day BETWEEN 1 AND 31)
OR (call_year = 2013 AND
call_month BETWEEN 1 AND 9 AND
call_day BETWEEN 1 AND 31)
OR (call_year = 2013 AND
call_month = 10 AND
call_day BETWEEN 1 AND 15)
Here is SQLFiddle demo
Upvotes: 3
Reputation: 8758
As PeterM said, this may play hell with performance, but if you're storing these as integers, you can do the following: (year * 10000) + (month* 100) + day will always yield an 8 byte "date".
2013 *10000 = 20130000
9 * 100 = 900
15
20130915
It's an ugly hack, and it will be expensive, because you'll be evaluating every row in your table, if you can't limit the rows in any other way, but I think it'll work.
EDIT: Typing is hard!
Upvotes: 1
Reputation: 18411
SELECT COUNT(*) FROM `import`
WHERE CONCAT(CAST(`call_year`AS CHAR(4)) , RIGHT(CONCAT('00',CAST(`call_month`AS CHAR(2))),2) , RIGHT(CONCAT('00',CAST(`call_day`AS CHAR(2))),2))
BETWEEN '20130929' AND '20131015'
Upvotes: 0
Reputation: 1830
Concat the values like yyyymmdd and then you can compare them like strings.
Upvotes: 5