MYSQL case statment comparing string values incorrect outcome - mysql

I have the following mysql query with what should be a simple case statement in comparing two srting values and populating whichever is the greater value as follows:
SELECT
wca.bond.secid,
wca.bond.IssueAmount,
wca.bochg.NewOutValue,
case when wca.bond.IssueAmount >= wca.bochg.NewOutValue
then wca.bond.IssueAmount ELSE wca.bochg.NewOutValue END AS IssueAmount2
from
wca.bond
INNER JOIN wca.bochg ON wca.bond.secid = wca.bochg.SecID
WHERE
bond.secid = 5955156;
The following output is produced:
As you can see from the output the case statment does snot seem to work as it should as I would expect to see 12200 as the value under IssueAmount2 as opposed to 7200. I thought it may be due to the fields being compared being different formats but I can confirm they are both Varchars having the same length 20.
Does anyone know why I'm having this issue?

In VARCHAR() datatype, it will compare the first character. If the first character is the same, it will compare the second character and continue the comparison until it found a difference. In your case, although it's obvious that 12200 is more than 7200, that's only true if we're comparing numbers. So, with varchar, this is what happen:
[7]200 = 7 is bigger than 1
[1]2200 = 1 is smaller than 7
So what you need is to convert the value into integer. Here's a suggestion to your current query:
SELECT
wca.bond.secid,
wca.bond.IssueAmount,
wca.bochg.NewOutValue,
case when wca.bond.IssueAmount+0 >= wca.bochg.NewOutValue+0
then wca.bond.IssueAmount ELSE wca.bochg.NewOutValue END AS IssueAmount2
from
wca.bond
INNER JOIN wca.bochg ON wca.bond.secid = wca.bochg.SecID
WHERE
bond.secid = 5955156;
Add +0 to the column in comparison to quickly make the column value identified as integer. You can also use CONVERT() or CAST():
CONVERT(varchar_val1, SIGNED) >= CONVERT(varchar_val2, SIGNED)
...
or
CAST(varchar_val1 AS SIGNED) >= CAST(varchar_val2 AS SIGNED)
...
Demo fiddle

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 : date format not working with OR in where condition

Whenever I'm using OR in where condition my query is putting date_format() it's working but when I'm using AND it's working fine.
True Query:
SELECT * FROM `tbl_inquiry_trans`
WHERE date_format(follow_updatetime,'%Y-%m-%d') >= '2018-08-02'
AND date_format(follow_updatetime,'%Y-%m-%d') <= '2018-08-02'
AND emp_id=2 or user_id=2
The above query should display specific date data but it's showing all dates data.
Test Query:
SELECT * FROM `tbl_inquiry_trans`
WHERE date_format(follow_updatetime,'%Y-%m-%d') >= '2018-08-02'
AND date_format(follow_updatetime,'%Y-%m-%d') <= '2018-08-02'
AND emp_id=2
When I'm using AND it's showing expected date data but I want to use OR in the where clause.
The and logical operator has a higher precedence than the or operator (i.e., and expressions are evaluated before or expressions, in a similar way you'd calculate a multiplication before calculating an addition in an arithmetic expression). In order to achieve the behavior you wanted, you need to surround the two sides of the or operator with parenthesis:
SELECT *
FROM tbl_inquiry_trans
WHERE date_format(follow_updatetime,'%Y-%m-%d')>='2018-08-02' AND
date_format(follow_updatetime,'%Y-%m-%d')<='2018-08-02' AND
(emp_id=2 OR user_id=2) -- Here
Same answer as #Mureinik, except that I don't think you need to those calls to DATE_FORMAT, because in MySQL it is possible to directly compare dates against string literals. So, the following should suffice:
SELECT *
FROM tbl_inquiry_trans
WHERE
follow_updatetime >= '2018-08-02' AND follow_updatetime < '2018-08-03' AND
(emp_id = 2 OR user_id = 2);
The logic in the above check on follow_updatetime is that any date would match if it were on or after midnight of 2018-08-02 or strictly before midnight of 2018-08-03. This would cover the entire day of 2018-08-02. This version of doing it is preferable to what you had, because it makes it possible to use an index on the follow_updatetime column.

SQL "BETWEEN" request not acting as I want

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'

DATEDIFF SQL Query

I am at the final stage of my project and have the problem to find if a job is overdue. I link this to priority for example if a job has a priority of 1 it must be complete in 1 day, a priority of 4 then 4 days.
I have come up with a CASE however this doesn't seem to work any help would be appreciated.
SELECT `defect_Id`,`Overtasked`
WHERE
CASE DATEDIFF(DD,`date_Investigaton` - `CURRENT_DATE()`) >= `priority` AS Overtasked
THEN `Overtasked` == 'YES'
ELSE `Overtasked` == 'NO'
END
Solution
`SELECT defect_Id,
CASE WHEN DATEDIFF(date_Investigated, CURDATE()) >= priority
THEN 'YES'
ELSE 'NO'
END AS Overtasked
FROM defect_report
WHERE defect_Id = '82'`
Appreciate the guidance you guys give!
You are completely mixing up SQL dialects and even there are syntax errors.
Assuming you are talking about MS SQL Server let's try this:
SELECT defect_Id,
CASE WHEN DATEDIFF(DD, date_Investigaton, getdate()) >= priority
THEN 'YES'
ELSE 'NO'
END AS Overtasked
FROM <YourTable>
WHERE <YourWhereIfAny>
If date_Investigation is a DATE column, the subtraction date_Investigation - CURRENT_DATE() produces the number of days you need.
Otherwise (if it is a DATETIME, for example) both operands are converted to float and the result is something you are totally not expecting. For such situations use the DATEDIFF() function. It interprets its arguments as DATE (ignores the time part) and returns the integer number of days between the two dates.
Your query should be like:
SELECT
`defect_Id`,
IF (DATEDIFF(`date_Investigaton`, CURRENT_DATE()) >= `priority`, 'YES', 'NO')
AS `Overtasked`
FROM [...your table name here...]
WHERE [...conditions...]
Replace the parts in square brackets ([...]) with the name of the table where to get the data from and some conditions to limit the number of returned rows (otherwise it will get the entire table which, most probably, is not what you want).
Btw, CURRENT_DATE() is also a function. If you write it in backquotes (``), MySQL will try to find a column with this name and it will fail.
Read the accepted answer for this question. It explains when to use back ticks, single quotes or double quotes in MySQL (and partially in PHP).

What does the use of an asterisk (*) mean in a mysql select statement, other than a wildcard?

I came across a mysql query that looks like this:
SELECT
SUM(some_amount*(some_field!=90)*(some_date < '2011-04-22'))
, SUM(some_amount*(some_field =90)*(some_date < '2011-04-22')*(another_field IS NULL))
FROM
some_table
What does the * mean in the select statement in this case?
Looks like CAST() is not necessary for boolean-to-integer conversions. Multiplication is used to convert the sum to 0 for unwanted rows (using the fact that boolean true can be cast to 1 and false to 0):
some_amount*(some_field!=90)*(some_date < '2011-04-22')
if some_field == 90 or some_date >= '2011-04-22', the corresponding term will evaluate to 0, thereby converting the entire expression to 0.
It is a multiplication operation.
example 2*3=6
It's a standard multiplication operator,
select 2 * 2
= 4
:)