Why is Between not finding the end date - mysql

I am using the following query:
SELECT *
FROM vaccines
WHERE (initial_date BETWEEN '1/1/2018' AND '10/31/2018')
AND customer_id='110607'
AND item_description= 'FLUZONE .QUAD MDV';
I get no results and there are entries for that date range, however, results are display till month 9, from 10 -12 I get nothing.

Q: Why is BETWEEN not finding the end date?
A: Likely because MySQL is doing string comparison; comparing string values not date values.
If we are comparing strings, note that any string starting with '9'
is going to be greater than a string starting with '1'.
'9........' is greater than '1........'
likewise
'9/23/1999' is greater than '10/31/2001'
Even if we include leading zeros, comparing as strings, we are still going to run into problems with comparing the year:
'09/23/2018' is less than '10/31/2001'
^^^^ ^^^^
If we want to make use of MySQL ability to compare date and datetime values, then we can use MySQL native DATE and DATETIME types, and avoid doing string comparisons.
If initial_date column is defined as DATE or DATETIME datatype, then specify date values in the query, e.g.
WHERE initial_date >= '2018-01-01' + INTERVAL 0 DAY
AND initial_date < '2018-11-01' + INTERVAL 0 DAY
(Note: MySQL date literals can be represented as 'YYYY-MM-DD'.)
If initial_date columns is defined as string type (e.g. CHAR or VARCHAR), then we can convert the string values into a date value using the STR_TO_DATE function e.g.
WHERE STR_TO_DATE(initial_date,'mm/dd/yyyy') >= '2018-01-01' + INTERVAL 0 DAY
AND STR_TO_DATE(initial_date,'mm/dd/yyyy') < '2018-11-01' + INTERVAL 0 DAY
(Note that the second argument is a format model; the string will be interpreted according to that model.)

Try
SELECT *
FROM vaccines
WHERE (initial_date BETWEEN CAST('2018-01-01' AS DATE) AND CAST('2018-10-31' AS DATE))
AND customer_id='110607'
AND item_description= 'FLUZONE .QUAD MDV';

Related

A more elegant way to get current year-month to insert in a 'where' clause

The table I'm querying from has this DateTime column.
created_time
2022-03-19T15:21:52+08:00
2022-03-19T13:10:22+08:00
2022-03-19T13:09:52+08:00
2022-03-19T13:02:47+08:00
2022-03-20T20:51:03+08:00
select extract(year_month from curtime())
Using extract (as above) doesn't work as it will get me: 202203.
SELECT created_time
FROM `freemark-prod-zohocrm`.patients p
where select extract(year_month from curtime())
Therefore the query above will not give me any result as my 'where' clause needs to specifically ask for '2022-03%' and not 202203.
SELECT created_time
FROM `freemark-prod-zohocrm`.patients p
where date_format(p.created_time, '%Y')=(select extract(year from curtime()))
and date_format(p.created_time, '%m')=(select extract(month from curtime()))
Therefore I am currently using the query above to obtain Year='2022' AND Month='03' which I feel doesn't look that nice and might cause me future problems.
I am wondering if there is a more elegant way to get the current 'Year-Month' (eg.'2022-03%') to use in my 'where' clause.
Thank you for your time.
LIKE Example db<>fiddle
Since the query is a simple YYYY-MM prefixed lookup, use LIKE DATE_FORMAT(NOW(), '%Y-%m-%%') as 2022-03-%. Functioning the same for either DATETIME or VARCHAR column data-types, and is by-far the fastest solution regardless of indexing.
SELECT p.created_time
FROM `freemark-prod-zohocrm`.patients p
WHERE p.created_time LIKE DATE_FORMAT(NOW(), '%Y-%m-%%');
Compare YEAR_MONTH Criteria db<>fiddle
To fix the issue with the original query not returning results, match the criteria column and value functions. However, as a function is called on the column value a full-table scan will be performed.
SELECT p.created_time
FROM `freemark-prod-zohocrm`.patients p
WHERE EXTRACT(YEAR_MONTH FROM p.created_time) = EXTRACT(YEAR_MONTH FROM NOW());
To prevent a full-table scan avoid altering column values in the criteria using DATE_FORMAT(created_time), EXTRACT(... FROM created_time) or other functions, which will cause MySQL to check all rows in the table to determine if the condition matches.
MySQL 5.5 and earlier db<>fiddle
Note: In MySQL 5.5 and earlier, extract(year_month from curtime()) or for any date specific Temporal Intervals will return NULL because curtime() returns the TIME portion as HH:MM:SS.The behavior appears to have changed in MySQL 5.6 and later, where EXTRACT() will apply to the current date when the date argument is supplied as a TIME data-type and failing when supplied as a time string literal.
However, an undesirable value will be returned when using a date + time interval such as DAY_MINUTE and the date portion of the value is omitted.
SELECT
curtime(), /* 19:07:40 */
extract(year_month from curtime()), /* NULL */
extract(day_minute from curtime()); /* 1907 */
To resolve the issue always use NOW(), otherwise in MySQL 5.5 and earlier curtime() should be replaced with CURDATE() or NOW() depending on the interval being used.
DATE Interpreted Example db<>fiddle
As DATE_FORMAT() returns a string literal, to prevent string comparison issues in MySQL such as '10' > '2' = false, enforce a DATE or DATETIME context.
When using DATE or DATETIME interpreted values (see explanation below) to retrieve the rows for an entire month, the following criteria values can be used to force the condition to process in the context of a DATE data-type
DATE(DATE_FORMAT(NOW(), '%Y-%m-01')) to get the first day of the current month as a DATE data-type
LAST_DAY(NOW()) + INTERVAL 1 DAY to get the the first day of the next month as a DATE data-type.
SELECT p.created_time
FROM `freemark-prod-zohocrm`.patients p
WHERE p.created_time >= DATE(DATE_FORMAT(NOW() ,'%Y-%m-01'))
AND p.created_time < LAST_DAY(NOW()) + INTERVAL 1 DAY;
LAST_DAY(NOW()) will return the date as 2022-03-31
+ INTERVAL 1 DAY will increment the date by one day to 2022-04-01
MySQL 5.6+ Results
CREATE TABLE patients_varchar (
`id` INTEGER NOT NULL PRIMARY KEY,
`created_time` VARCHAR(25),
INDEX(`created_time`)
);
INSERT INTO patients_varchar
(`id`, `created_time`)
VALUES
('1', '2022-02-19T15:21:52+08:00'), /* Added to verify range */
('2', '2022-03-19T15:21:52+08:00'),
('3', '2022-03-19T13:10:22+08:00'),
('4', '2022-03-19T13:09:52+08:00'),
('5', '2022-03-19T13:02:47+08:00'),
('6', '2022-03-20T20:51:03+08:00'),
('7', '2022-03-31T20:51:03+08:00'),
('8', '2022-04-20T20:51:03+08:00'); /* Added to verify range */
created_time
2022-03-19T13:02:47+08:00
2022-03-19T13:09:52+08:00
2022-03-19T13:10:22+08:00
2022-03-19T15:21:52+08:00
2022-03-20T20:51:03+08:00
2022-03-31T20:51:03+08:00
VARCHAR and Date Time Literals Explanation
When the column data-type is VARCHAR using a valid date time string literal format such as YYYY-MM-DDTHH:MM:SS+08:00, MySQL will automatically interpret the column value format of YYYY-MM-DDTHH:MM:SS+MM:HH as a DATETIME data-type appropriately when provided a criteria value in the DATE or DATETIME data-type context.Please see the String and Numeric Literals in Date and Time Context for more details.
DATETIME context and time zone offsets
For MySQL 5.6+ to specify the inclusion of a time value, use TIMESTAMP(DATE_FORMAT(LAST_DAY(NOW()), '%Y-%m-%d 23:59:59')) to force a DATETIME context as opposed to using DATE().
For MySQL 5.5 and earlier db<>fiddle, when specifying a DATETIME context, the time zone offset in the column value is not parsed correctly and produces a different result. Using a string context of DATE_FORMAT(LAST_DAY(NOW()), '%Y-%m-%dT23:59:59') resolves the issue but may produce unexpected results, due to the string context comparison eg: '10' > '2' = false.
Note: the T is required for MySQL to parse the YYYY-MM-DDTHH:MM:SS formatted column value correctly.
For example the following conditions will all return true due to the DATETIME context. While MySQL 8.0+ will process the time zone offset when it is included in the string.
SELECT
'2022-02-19T15:21:52+08:00' = TIMESTAMP('2022-02-19T15:21:52'),
'2022-02-19T15:21:52+08:00' < TIMESTAMP('2022-02-20T15:21:52'),
'2022-02-19T15:21:52+08:00' > TIMESTAMP('2022-02-18T15:21:52'),
'2022-02-19T00:00:00+08:00' = TIMESTAMP('2022-02-19'),
'2022-02-19T15:21:52+08:00' < TIMESTAMP('2022-02-20'),
'2022-02-19T15:21:52+08:00' > TIMESTAMP('2022-02-18');
As opposed to the following conditions comparing strings that all return unexpected results.
SELECT
'2022-02-19T15:21:52+08:00' = '2022-02-19T15:21:52', # false
'2022-02-19T15:21:52+08:00' <= '2022-02-19T15:21:52', # false
'2022-02-19T15:21:52+08:00' > '2022-02-19T15:21:52'; # true
querying based on function calls such as extract(), or others datepart(), etc. are not Sargeable
What you would be better doing is something like
where
created_time >= '2022-03-01'
AND created_time < '2022-04-01'
This way, it gets the entire month in question including time portion up to 2022-03-31 # 11:59:59pm.
Now, to compare automatically against whatever the current date IS, you can do with MySQL Variables to compute the first of the month and beginning of next month for your from/to range.
select
...
from
( select #FirstOfMonth := DATE_FORMAT(CURDATE(), '%Y-%m-01'),
#FirstOfNextMonth := date_add( #FirstOfMonth, interval 1 month )) sqlvars,
`freemark-prod-zohocrm`.patients p
where
p.created_time >= #FirstOfMonth
AND p.created_time < #FirstOfNextMonth

How to cast this integer value to date in MySQL

i have airbnb data and i want to cast column last_reviews (which datatype is int) to date
this is my sql code http://sqlfiddle.com/#!9/b5ea42/31
how do i cast int > date?
or how do i create last_reviews column as datatype date?
The last_review date seems to be the number of days since 1900-01-01 so in MySQL you would:
SELECT '1900-01-01' + INTERVAL last_review DAY AS last_review_date
FROM ...
The result checks out for the sample data (which you should delete).
last_review values looks like VB(A) numeric representation for dates. If so then test this:
SELECT last_review, '1900-01-01' + INTERVAL last_review DAY
FROM airbnb
Adjust constant part (maybe it must be '1899-12-31'?) if needed.

Unexpected result when comparing dates

I have a table with a column of type date (yyyy-mm-dd h:i:s) on a MYSQL 5.7.26 server.
When I query like this:
SET #THIS_YEAR = "2019-01-01 00:00:00";
SELECT * FROM table WHERE `date` > year(#THIS_YEAR);
I am expecting all entries with date since 2019-01-01.
But I also get entries from years before.
But this query works is:
SELECT * FROM table WHERE `date` > "2019";
Btw this query also returns the wrong (or rather unexpected) results:
SELECT * FROM table WHERE `date` > 2019;
What is the difference between these queries? Why doesnt it work with a variable or when comparing the date to an integer? I assume its somekind of auto type conversion?
MySQLs implicit type conversion can be very surprising. If you want to understand the behavior of your queries, you can try to apply the type conversion rules as described in Type Conversion in Expression Evaluation. However - I failed to do that for your case. For example: For the two expressions date > '2019' and date > 2019 I would apply the following rule:
If one of the arguments is a TIMESTAMP or DATETIME column and the
other argument is a constant, the constant is converted to a timestamp
before the comparison is performed.
But that cannot be the case, because neither the number 2019 nor the string '2019' can be converted to a temporal type. Here is a query, which demonstrates some implicit conversions:
select '2019' + interval 0 day -- implicit cast to date(time)
, 2019 + interval 0 day
, 20190101 + interval 0 day
, 190101 + interval 0 day
, '2019*01*01' + interval 0 day
, '2019-01-01' + interval 0 day
, '2019-01-01' + 0 -- implicit cast to numeric
, date('2019-01-01') + 0
, date('2018-01-01') > 2019
, date('2018-01-01') > '2019'
;
Result:
Expression | Result
------------------------------|-----------
'2019' + interval 0 day | null
2019 + interval 0 day | null
20190101 + interval 0 day | 2019-01-01
190101 + interval 0 day | 2019-01-01
'2019*01*01' + interval 0 day | 2019-01-01
'2019-01-01' + interval 0 day | 2019-01-01
'2019-01-01' + 0 | 2019
date('2019-01-01') + 0 | 20190101
date('2018-01-01') > 2019 | 1
date('2018-01-01') > '2019' | 0
As you see, when we try to convert 2019 or '2019' to a date (or datetime), we get NULL. Thus the conditions should also be evaluated to NULL and the result set would be empty. But as we know, that is not the case. Maybe I'm just wrong, assuming that 2019 and '2019' are constants. But then I don't know what they could mean.
So I can only make assumptions. And my assumtion is: Whenever one comparator is numeric, the other value is also converted to a numeric value. This would be the case for date > 2019 aswell as for date > year(#THIS_YEAR). In this case the date 2018-01-01 is converted to 20180101 (see the table above), which (in numeric context) is greater than 2019. So you get rows from the year 2018.
For date > '2019' I can only assume, that the values are compared as strings. And '2018-01-01' as string is considered "smaller" than 2019.
But even if that behavior would be properly documented, the rules are too difficult to remember, because one can hardly see any logic behind them. (I don't say - there is no logic - I just don't see any.)
So I can give you one advise: If you want to compare two incompatible types, always cast or convert them to be compatible.
WHERE year(date) >= year(#THIS_YEAR)
would be fine, since you compare two numeric values. But that is not necessery in your case and you can just use
WHERE date >= #THIS_YEAR
because 2019-01-01 00:00:00 in
`SET #THIS_YEAR = "2019-01-01 00:00:00";`
is a perfectly formatted DATETIME string and can be considered compatible with the DATETIME type. '2019-01-01' would be just fine aswell.
Note that if you wrapp a column into a function call (like year(date)) you will loose the ability to use an index on that column.
The underlying problem is one of data types.
In short words: The interpreter has a hard time knowing if your "2019" is a string or a date. So it goes by what it expects there to be.
If you store the value into a variable, it is parsed as a string, because a string is expected here. If you compare it directly, a date value is expected - and so MySQL tries to parse a date, successfully.
2019, as opposed to "2019" clearly is a number, not a date value. So that explains funny behaviour there.
This query will compare the year from #THIS_YEAR and date:
SET #THIS_YEAR = "2019-01-01 00:00:00";
SELECT * FROM table WHERE year(`date`) = year(#THIS_YEAR);
Notice date is wrapped in the year function. This means that you are now comparing year() to year().
This query works
SELECT * FROM table WHERE `date` > "2019";
Because the interpreter will use a character representation of date and perform character by character comparison: "2019-" > "2019"
This query doesn't work:
SELECT * FROM table WHERE `date` > 2019;
For the same reason. The interpreter will use a character representation of date and then compare the first character to 2019. "2" < 2019 (both in terms of human readable and numeric representation of the character 2
And this query:
SET #THIS_YEAR = "2019-01-01 00:00:00";
SELECT * FROM table WHERE `date` > year(#THIS_YEAR);
Has the same problem. year() returns a number so it will again be: "2">2019

Losy comparison between dates

Is there a way I can make MySQL return 1 instead of 0 for SELECT NOW() = '2016-10-10' without casting (CAST('2016-10-10' AS DATE)) or converting to date (DATE('2016-10-10')).
My real case scenario is a comparison between a DATE and a DATETIME column. I want to JOIN on those columns, but that's possible only if I can make MySQL compare only the date, ignoring the time.
I can't do the cast/convert because that is very expensive ( Slow query performance left joining a view ).
It's not the '2016-10-10' string that you need to cast (since it is a valid date literal), but NOW().
NOW() returns your current timestamp, with hours, minutes and seconds. While '2016-10-10' is interpreted as '2016-10-10 00:00:00'. Which, presumably is not equal to the current time.
So
SELECT DATE(NOW()) = '2016-10-10'
UPD:
I can make MySQL compare only the date, ignoring the time.
For the comparison coldate = coldatetime you can compare on range, like:
coldate <= coldatetime AND coldate + INTERVAL 1 DAY > coldatetime
Depending on your actual case it may or may not be beneficial.

Want to run a query which gives me results between two dates. I have timestamp in unix epoch format

I have got timestamps in epoch UNIX format. I want to run a query by directly giving date and not timestamp. How is that possible?
SELECT FROM_UNIXTIME(timestamp)
FROM report_data
WHERE timestamp = '1399376713'
I used this to convert to human readable format.
My database is something like this
timestamp event_type flags
1399357862 701 null
I want to give a particular date in my query and get the result.
It's possible using the FROM_UNIXTIME function.
This assumes that your table contains columns in DATETIME or TIMESTAMP, and you are wanting to supply 32-bit integer values in the query.
For example:
SELECT ...
FROM mytable t
WHERE t.datetime_col >= FROM_UNIXTIME( ? )
AND t.datetime_col < FROM_UNIXTIME( ? )
The integer values supplied as arguments to the FROM_UNIXTIME function will be interpreted as unix-style "seconds since epoch" integer values, and be converted to a DATETIME value using the current timezone setting of the client connection.
This approach will enable MySQL to use a range scan operation using an index with a leading column of datetime_col.
What's not at all clear is what the datatype of your column is, and what values you want to supply in the query. If the columns is datatype DATE, DATETIME or TIMESTAMP (which would be the normative pattern for storing date/time data), then you can specify date literals in standard MySQL format, 'YYYY-MM-DD HH:MM:SS'.
WHERE t.timestamp_col >= '2015-02-11 07:00'
AND t.timestamp_col < '2015-02-11 23:30:00'
If you are storing the "timestamp" as an integer value, then you will need the right side of the predicates to return an integer value, e.g.
WHERE t.integer_col >= UNIX_TIMESTAMP('2015-02-10')
AND t.integer_col < UNIX_TIMESTAMP('2015-02-10' + INTERVAL 24 HOUR)