SQL "BETWEEN" request not acting as I want - mysql

I'm having some issues with my SQL request.
Here's the request :
SELECT SUBSTR(`Date`, 1, 11) AS `format_date`
FROM table
HAVING `format_date` BETWEEN '07/06/2016' AND '16/06/2016'
When I run the request I get not wanted results like "07/11/2014". After doing some tests it looks like the request is only taking the day in consideration but I can't really figure out why. Any ideas ?

07/11/2014is between the two given strings. You are comparing strings, not dates, but you are getting exactly what you ask for.
This is what you compare:
"07/0" < "07/1" < "16/0"
Try comparing actual dates, or format your string so that you can use them (YYYY/MM/DD).
This question (and its accepted answer) should help you convert your strings to useable dates:
how-to-convert-a-string-to-date-in-mysql
You can then copmpare real dates with each other.

The reason what you get wrong result is clear already, you can try following;)
SELECT SUBSTR(`Date`, 1, 11) AS `format_date`
FROM table
HAVING STR_TO_DATE(`format_date`, '%d/%m/%Y') BETWEEN STR_TO_DATE('07/06/2016', '%d/%m/%Y') AND STR_TO_DATE('16/06/2016', '%d/%m/%Y')
And I think you should change HAVING to WHERE.

Your problem can be summed up in the following expression...
SELECT '8' BETWEEN '7' AND '77';
+--------------------------+
| '8' BETWEEN '7' AND '77' |
+--------------------------+
| 0 |
+--------------------------+

There are 3 problems to address.
The simplest is that you have wrongly used HAVING instead of WHERE.
You have converted dates into strings in the form dd/mm/yyyy. The problem here is that text (strings) do NOT behave like numbers e.g. in text 1 and 10 are sorted before 2 or 3. Due to this you are asking for TEXT between STRINGS THAT LOOK LIKE DATES but it will not behave thay way because it requires handling dates as numeric.
Between is a very poor way to handle date ranges. A far more reliable way is to use a combination of >= and < as seen below.
Example
SELECT SUBSTR(Date, 1, 11) AS `format_date`
FROM table
WHERE `date` >= '2016-06-06'' AND `date` < '2016-06-17'

Related

Postgresql json field has dates in both YYYY-MM-DD and 13 digit unix timestamp format

I am parsing a json array and one field I am pulling out is closedate. However closedate has two different date formats one is YYYY-MM-DD and the other is a 13 digit timestamp. I am trying to get consistent formatting of the dates as well as have it be an integer compared to a string. Right now the query returning the close date is:
json_array_elements(ld.data->'Table1'->'Details')->>'closeDate' as closedate
and it returns close date as a string:
id
closedate
1
2021-09-29
2
1606824000000
Someone was telling me to do something like a case statement with regex. But I am not familiar with regex function. Any help is appreciated.
Edit: I have
case when x.closedate::text ~* '^[0-9]{13}$' then
to_timestamp(x.closedate::bigint/1000)
when x.closedate = '0' then null
when x.closedate = '' then null
else
to_date(x.closedate,'MMDDYYYY') end as transactionclosedate
the case statement works for converting the 13 digit timestamp to a date but I am getting the error:
ERROR: date/time field value out of range: "2020-10-23"
when trying to convert the date strings in the correct format to dates in the else part of the case statement.
An example of one way to make this work. My regex skills are not strong so others may have a better solution:
create table regex_test (id int, fld_1 varchar);
insert into regex_test values (1, '1606824000000'), (2, '2021-09-29');
select * from regex_test ;
id | fld_1
----+---------------
1 | 1606824000000
2 | 2021-09-29
select
id,
case when fld_1 ~* '^[0-9]*$' then
to_timestamp(fld_1::bigint/1000)
else
fld_1::timestamp end as ts_fld
from
regex_test;
id | ts_fld
----+------------------------
1 | 2020-12-01 04:00:00-08
2 | 2021-09-29 00:00:00-07
I hope this query help you
with data as (
select
json_array_elements(data->'Table1'->'Details')->>'closeDate' as closedate
from your_table
)
select
case when closedate::text ~ '^[0-9]+$' then
to_timestamp(closedate::numeric / 1000)::date
else
closedate::date
end
from data;
Either of the other answers would be ok providing that only the specified formats exist. However, containing those formats requires a text field; which may contain anything. It is dangerous to assume if the content is not 13 digits then it is a valid formatted ISO date. I would validate that as well (and verify digits length).
select id,
, case when closedate ~* '^[0-9]{13}$' then
to_timestamp(closedate::bigint/1000)
when is_valid_iso_date(closedate) then
closedate::timestamp
else
'-infinity'::timestamp -- or whatever to indicate Invalid Date.
from <your table> ;
The problem being that is_valid_iso_date function. It turns out however I had to create just that a couple years ago, I'll make the result available here.
DISCLAIMER: While the function has given no known erroneous results it has NOT been exhaustively tested.

MySQL Query Group By Date

I have tried various recommendations based off of other posts with no avail.
I have a database scheme of records with a Created_Date Key, and Value would be 01/01/2017
I am trying to query the database records to give a returned count of How many records per month and which month those fall in line with.
With the following
SELECT SQL_NO_CACHE MONTH(`Created_Date`), COUNT(*)
FROM `CRM_Leads`
GROUP BY MONTH(`Created_Date`)
I return
MONTH(`Created_Date`) COUNT(*)
NULL 872
I have also tried almost all the variations on the following post
Count records for every month in a year
Any help would be appreciated.
assuming your created_date is a string of format ('dd-mm-yyyy') the you should convert as date with str_to_date
SELECT SQL_NO_CACHE MONTH(str_to_date(`Created_Date`, '%d/%m/%Y')), COUNT(*)
FROM `CRM_Leads`
GROUP BY MONTH(str_to_date(`Created_Date`, '%d/%m/%Y'))
For as long as you store date/time information as strings, you will have great difficulty using any date/time specific functions and features. If you are getting NULL from MONTH(str_to_date(Created_Date, '%d/%m/%Y')) then the str_to_date isn't converting the strings to dates and the most likely reason for this is the d m y "pattern" is not corrrect.
All you have old us about your "strings that might be dates" is that one of them looks like this: 01/01/2017. Now that could be DD/MM/YYYY or MM/DD/YYYY and we simply cannot tell which one is correct from the single value you have chosen to share with us. Look for any day value greater then 12 in your data e.g. 17/01/2017 ==> DD/MM/YYYY or 01/17/2017 ==> MM/DD/YYYY
Once you have made the choice of which pattern your "strings that might be dates" follow; apply that pattern in the str_to_date() function. You migh want to try a few different patterns to get the best one (and these are just 3 of many you could try):
# which pattern is best for you?
SELECT Created_Date
, str_to_date(`Created_Date`, '%d/%m/%Y') "d/m/y"
, str_to_date(`Created_Date`, '%m/%d/%Y') "m/d/y"
, str_to_date(`Created_Date`, '%Y-%m-%d') "y-m-d"
FROM `CRM_Leads`
You will not have success with your group by query until you choose the most appropriate d m y pattern to apply in teh str_to_date function. Note here that you might also have a variety of patterns in your data, in which case you have an even bigger problem to solve.
Once you have made the choice of which pattern your "strings that might be dates" follow; apply that pattern in the str_to_date() function and ONLY THEN your group by query will work.

Where clause with dateformat and other variables

SELECT * FROM `siparisler` WHERE `cafe_id` = 3 AND DATE_FORMAT(tarih,'%Y-%m-%d') BETWEEN 2017-08-01 AND 2017-08-06
My dates are listed like this on my table (tarih) 2017-02-22 15:28:33
so I want to retrieve data when cafe_id is 3 and tarih between those two dates.
But when I run this query I get zero results.
What may be the problem. I could not find any solution.
DATE_FORMAT() is used on a date data type. You don't need it. You only need the quotes around the constants:
SELECT s.*
FROM siparisler s
WHERE s.cafe_id = 3 AND
s.tarih >= '2017-08-01' AND
s.tarih < '2017-08-07'
Note that I changed the condition from BETWEEN to direct comparisons. This is on purpose. This logic will work regardless of whether tarih has a time component.
You missed to quote the date string literal. It should be
BETWEEN '2017-08-01' AND '2017-08-06'

DATE type comparison in MySQL

Had a bit unintuitive case right now with MySQL:
the query contains where clause with comparison: WHERE t.date = '2016-12-31' (t.date-s datatype is DATE(!)).. And it returns no records on execution. But the query: WHERE t.date > '2016-12-31' - returns the records with date equals '2016-12-31' among other records! The record for 2016-12-31 also showed up in case I've used BETWEEN '20161231' AND '20170101'. Tried formattings, type changes - nothing helped. After some time spent on searching for cause I did the following: updated the record's date column manually, SETting it to '2016-12-31'. After this action WHERE t.date = '2016-12-31' started to work as expected.
Probably I'm missing something, wondering what can cause such behavior.
Update
date is DATE, not DATETIME
After doing manual update I can't reproduce the mentioned behavior again: now any type of comparison(=, DATE(..)=, STRCMP) - works as it should!
Update 2
For 2016-11-30 and 2016-09-30(end of months!) found the same behavior! Won't update the record manually for now to test the suggestions I get here.
Update 3
I've also run OPTIMIZE TABLE on the table with that date column to rebuild indexes for elimination any problems with corruption.
Update 4
Here is more:
if I check HEX values for the date field for incorrect fields(end of month) I get wrong values!
SELECT HEX(t.date) FROM table t WHERE t.date BETWEEN DATE('20160930') AND DATE('20161001');
Returns:
323031362D31302D3030
323031362D31302D3031
SELECT HEX(DATE('20160930'));
Returns:
323031362D30392D3330
And 323031362D30392D3330 != 323031362D31302D3030
SELECT X'323031362D31302D3030';
And it returns:
2016-10-00, NOT 2016-09-30!
For the value that I've updated manually - HEX is same.
But what can cause such difference?
Try forcing the format using
WHERE date(t.date) = '2016-12-31'
or
WHERE date(t.date) = str_to_date( '2016-12-31', '%Y-%m-%d')
or based on your test
WHERE date(t.date) = str_to_date( '20161231', '%Y%m%d')
After some investigation I've found the problem and its not related directly to the date comparison in MySQL. I'll post it here in case anyone is stuck at such case.
I've found that the problem was with selecting results in IDE (in my case DataGrip): the value for date field in database was 2016-10-00 and select was returning 2016-09-30! That was confusing.. But after the 00 DAY was found - it was relatively easy to find the cause of it: CURDATE() - 1 (in my case there should have been: CURDATE() - INTERVAL 1 DAY). Don't ever use date related functionality without specific functions like INTERVAL!!
Thanks to everyone who supported the question, sorry for confusion, I was confused too and found the answer only after several steps.

Mysql Select with Dates and maybe Case when

im having a problem where i cant think of a solution, maybe im having a bad table-structure or i just dont know enough about mysql select commands to think of a good solution. Maybe you can help me out:
So i got a table that has a Column with the Date-format (yyyy-mm-dd) i wanted to select all upcoming dates so i did:
SELECT * WHERE date >= now.
This worked kinda well but i also got "dates" where only the year is entered (2014-00-00) i also wanted to select these but "now" is already bigger so i made another column with the year only and if the month, date or both arent known i will use 0000-00-00 and the Column "year" now i could select like this:
SELECT * WHERE date >= now AND year >=now(year)
Now all entrys with 0000-00-00 wont be selected. If i use OR the entrys from last year will be shown.
So thats my problem, is there any way i can change my table so i can have entries with only the year or only year and month and of course all together? I already considered get rid of the date-format and use simple INT with seperated columns for year, month and date. But i think i will have the same problem.
Sometimes i just want to do a capsuled select like
SELECT *
WHERE (date >= now AND year >= now(year))
OR date == "0000-00-00" (i know that this doesnt work)
If I understood your problem correctly, you could use this request:
WHERE (date >= now OR year > now(year))
There is probably a simpler way though, that would preserve your design, like initializing at January 1st (01-01) instead of 00-00
I think you can use this code:
$_SESSION['month'] = //set here your selected month
$_SESSION['year'] = //set here your selected year
SELECT * FROM table WHERE DATEPART(m,date) >= '".$_SESSION['month']."' AND DATEPART(yyyy,year) >= '".$_SESSION['year']."' AND date <> '0000-00-00'
Change your table structure format. Actually just allow for that field to have null value when not entered. By default it will be null then. You shouldn't be storing 0000-00-00 as a value for Date type field. I would rather leave it as null , or as suggested in some of previous answers, initialize it with some other date. It would be much easier to manipulate with database then.
the problem is that half of you write is not MySQL and your database schema is terrible...
You have the following problems:
column data date does not have the date data type.
To fix it, you need to add a cast to the select statement eg. cast(datecolumn as date)
select * from table where cast(datecolumn as date) >= '2014-01-10';
the way to use now date is using the now function.
select now(), date(now());
result> 2014-01-10 11:11:36, 2014-01-10
select * from table where cast(datecolumn as date) >= date(now());
Because your datecolumn is not a date (2014-00-00 is not a valid date), you need to use string manipulation to extract the year.
select substring('2014-01-01', 1,4)
result> 2014
select * from table where substring(datecolumn, 1,4) = year(now());
The comparassion operator is = and not ==
the select statement syntax looks like this (pay attention because you are missing the table in your statement)
select * from [Table] where [column] = condition ...
You probably need or instead of ands, therefore your query should look like this:
select * from FooTable where
cast(datecolumn as date) >= date(now())
or substring(datecolumn, 1,4) >= year(now())
or datecolumn = '0000-00-00'
You should use something like phpmyAdmin or mySQL workbench to test your sql queries before try to use them on php, java or whatever is your programing language.