MYSQL CONV from Hexadecimal to Decimal issue - mysql

I have some code that converts numbers from Hexadecimal to Decimal, and checks to see if greater than or equal to. Today, I discovered this doesn't always work, and I have no idea why.
I have tried using the raw values, and that works. Conversions outside of the compare work.
select case when 5 >= 40 then 'Fail' else 'Pass' end as 'Check';
select case WHEN CONV('0005',16,10) >= CONV('28',16,10) THEN 'Fail' else 'Pass' end as 'Check';
select CONV('0005',16,10) as '1', CONV('28',16,10) as '2';
The first select works as intended (Pass). The second select does not. The third select just shows that the conversion is working properly (i.e. 0005 HEX = 5 Decimal, and 28 Hex = 40 Decimal).
What am I missing?

From the docs:
CONV(N,from_base,to_base)
... Returns a string representation of the
number N ...
The results are strings and compared as such. For strings '5' > '40'. So you need to cast the ressults either explicitly with CAST
select case
WHEN CAST(CONV('0005',16,10) as SIGNED) >= CAST(CONV('28',16,10) as SIGNED )
THEN 'Fail'
else 'Pass' end
as 'Check';
or implicitly with a numeric operation like +0
select case WHEN CONV('0005',16,10)+0 >= CONV('28',16,10)+0 THEN 'Fail' else 'Pass' end as 'Check';
If you know the max length of the HEX strings, you could also fill them with zeros using LPAD()
select case
WHEN lpad('0005', 20, 0) >= lpad('28', 20, 0)
THEN 'Fail'
else 'Pass'
end as 'Check';

Related

Adding a single Digit to a 4-digits long number in mysql

I'm trying to get a mysql script, that changes every 4-digit long number "into" a 5-digit long, by adding a "0" at the start of each number. This is, what I tried:
SELECT * FROM `customer_address_entity_text` WHERE CHAR_LENGTH(value) < 5;
SELECT CONCAT("0", CAST(value as CHAR(50)) AS value;
but it shows an error, that there is no field "value" found:
#1054 - Unknown field 'value' in field list (translated)
would be nice, if someone could help me with this.
(it also gives out this error, when I'm not trying to Cast 'value' to a CHAR)
tl;dr: I want 'value = "0" + value' in mysql
example:
'value = 1234; value = "0" + value; value = 01234' and that in mysql
Two issues:
There is a missing closing parenthesis for CONCAT(
Your second SELECT has no FROM clause, so indeed there is no value field there.
So move that CONCAT expression inside the first SELECT clause and balance the parentheses:
SELECT c.*, CONCAT("0", CAST(value as CHAR(50))) AS value
FROM `customer_address_entity_text` c
WHERE CHAR_LENGTH(value) < 5;
If your purpose is to pad all values with zeroes so they get 5 digits, so that it also transforms 1 to "00001" and 12 to "00012", then use LPAD:
SELECT c.*, LPAD(value, 5, "0") AS value
FROM `customer_address_entity_text` c;
To update the value field:
UPDATE `customer_address_entity_text`
SET value = LPAD(value, 5, "0");
Or, with your original concat version:
UPDATE `customer_address_entity_text`
SET value = CONCAT("0", CAST(value as CHAR(50)))
WHERE CHAR_LENGTH(value) < 5;

SQL EXTRACT returns HOUR_MINUTE without colon

I am trying to format my timestamp date to get only hour with minutes from it. The problem is that the returned format is weird "825-2100" when it should be "8:25-21:00". So the output should be with colon but it is without.
This is the select I am doing:
CASE WHEN (TRUE) then CONCAT('Aeg ',EXTRACT(HOUR_MINUTE FROM fp.valid_from), '-', EXTRACT(HOUR_MINUTE FROM fp.valid_to) else 0 end,
Why I get format without colon?
Use format if you want a string. Also, all branches of the CASE should return a string (or NULL):
(CASE WHEN (TRUE)
THEN CONCAT('Aeg ',
FORMAT(fp.valid_from, '%H:%i%'),
'-'
FORMAT(fp.valid_to, '%H:%i%')
)
ELSE ''
END)

Unintended behavior: Subtraction between null values results in '0'

When either one of the field is NULL, I want the returned value to be NULL as well. I have also tried reversing the logic: is not null. Still the same results.
MySQL code:
(case
when
((`creative_stg_sample_tracking_raw`.`total_samples_received` is not null)
and (`creative_stg_sample_tracking_raw`.`total_samples_forecasted` is not null))
then
(cast(`creative_stg_sample_tracking_raw`.`total_samples_received`
as signed) - cast(`creative_stg_sample_tracking_raw`.`total_samples_forecasted`
as signed))
else NULL
end) AS `received_forecasted_dif`
Screenshot:
Your code should be working, but you don't need the case. Whenever one of the values is NULL, the expression should be NULL:
(cast(`creative_stg_sample_tracking_raw`.`total_samples_received` as signed) -
cast(`creative_stg_sample_tracking_raw`.`total_samples_forecasted` as signed))
) AS `received_forecasted_dif`
I wonder if your problem is that the value is actually 'NULL' rather than NULL. That is, a string value rather than a real NULL. MySQL will treat the string as 0 in the arithmetic.
You can fix this by doing:
(case when `creative_stg_sample_tracking_raw`.`total_samples_received` <> 'NULL' and
`creative_stg_sample_tracking_raw`.`total_samples_forecasted` <> 'NULL'
then (cast(`creative_stg_sample_tracking_raw`.`total_samples_received` as signed) -
cast(`creative_stg_sample_tracking_raw`.`total_samples_forecasted` as signed))
)
else NULL
end) AS `received_forecasted_dif`

Using CASE Function with DATEDIFF and COUNT

Hi I'm sure this is a simple fix but I cannot figure it out. I'm trying to label records that are overdue the completion date (CompleteDate-CurrentDate) (these numbers will be negatives) to "Overdue" for a report. I would also like the records to not be altered for the numbers that are not negatives. Here is a snippet of the code which is currently giving me NULL entries
Select CASE DATEDIFF(targetcompletedate, NOW())
When count(*) <=0 then 'Overdue'
END 'Days Left',
Any help would be greatly appreciated
Thanks
There are two variants of CASE:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
ELSE result_else
END
CASE scalar_expression
WHEN value1 THEN result1
WHEN value2 THEN result2
...
ELSE result_else
END
In your case, it should be the first syntax, because you are not comparing to specific values but to a range. Instead, your query is actually using the second syntax. The count(*)<=0 expression is evaluated to a boolean which is then implicitly converted to an integer, the type implied by the DATEDIFF result.
You just need to use the first syntax, something like this:
select case when targetcompletedate is null
then 'Not set'
when DATEDIFF(targetcompletedate, NOW()) <= 0
then 'Overdue'
else DATEDIFF(targetcompletedate, NOW())
end as 'Days Left'
I would suggest that you eliminate the datediff() entirely:
Select (CASE when targetcompletedate <= NOW() the 'Overdue' else 'Days Left' end)
If you want to show things as numbers, then you want the datediff(). For clarity, I would explicitly convert to character strings:
select (case when targetcompletedate <= NOW() then 'Overdue'
else cast(DATEDIFF(targetcompletedate, NOW()) as varchar(255))
end)
Or, perhaps:
select (case when targetcompletedate <= NOW() then 'Overdue'
else concat(DATEDIFF(targetcompletedate, NOW()), ' days left')
end)
The philosophy being: don't use a function if there is a simpler and clearer way to express what you want.
However, I wonder if you want to count the number in each group:
select sum(case when targetcompletedate <= NOW() then 1 else 0 end) as NumOverdue,
sum(case when targetcompletedate <= NOW() then 0 else 1 end) as NumWithDaysLeft
Just try this code:
SELECT
CASE
WHEN datediff(dd,targetcompletedate,now()) <= 0 THEN 'Overdue'
WHEN datediff(dd,targetcompletedate,now()) > 0 THEN 'Days_left'
ELSE NULL END,
datediff(dd,targetcompletedate,now()) AS 'days'
FROM tablename
The output is like:
Overdue -23
Days_left 13

mysql ORDER BY and CASE casts INT to CHAR?

For a stored procedure with configurable sort order (_Sort parameter), i use code like this:
SELECT * FROM distances
ORDER BY
CASE _Sort
WHEN 1 THEN uid
WHEN 2 THEN NULL
WHEN 3 THEN name
WHEN 4 THEN NULL
WHEN 5 THEN distance
WHEN 6 THEN NULL
ELSE distance
END ASC,
CASE _Sort
WHEN 2 THEN uid
WHEN 4 THEN name
WHEN 6 THEN distance
ELSE NULL
END DESC
in which uid is INT and distance is DOUBLE.
But if _Sort = 1, uid is ordered like it was a CHAR e.g.
200
207
25
4
Same thing for distance.
Casting to unsigned and decimal did not help.
ORDER BY uid ASC does the right thing, i.e. 4, 25, 200, 207
Any idea?
As documented under Control Flow Functions:
The return type of a CASE expression is the compatible aggregated type of all return values
Whilst the manual does not explicitly document how the "compatible aggregated type" is determined, one can follow the source from Item_func_case::fix_length_and_dec() through agg_result_type() to item_store_type():
static Item_result item_store_type(Item_result a, Item *item,
my_bool unsigned_flag)
{
Item_result b= item->result_type();
if (a == STRING_RESULT || b == STRING_RESULT)
return STRING_RESULT;
else if (a == REAL_RESULT || b == REAL_RESULT)
return REAL_RESULT;
else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT ||
unsigned_flag != item->unsigned_flag)
return DECIMAL_RESULT;
else
return INT_RESULT;
}
One can therefore see that if a single return value is a string, then the return value of the overall CASE expression will also be a string.
In your case, one presumes that name is a string; therefore the data type returned by your CASE expression is a string. Consequently, your numeric values are compared as strings and thus sorted lexicographically (hence the output that you observe).
One way of overcoming this would be to pad all numeric values to equal widths in order that sorting lexicographically will deliver the desired results: using the ZEROFILL attribute of integer-type columns will do this automatically; however, this is still pretty inefficient and you may wish to consider redesigning your logic.
For example, you could instead build a string that contains the desired SQL; then prepare and execute a statement from that string:
SET #sql := CONCAT(
'SELECT * FROM distances ORDER BY ',
CASE _Sort
WHEN 1 THEN 'uid ASC'
WHEN 2 THEN 'uid DESC'
WHEN 3 THEN 'name ASC'
WHEN 4 THEN 'name DESC'
WHEN 5 THEN 'distance ASC'
WHEN 6 THEN 'distance DESC'
ELSE 'distance ASC'
END
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Try this solution (edited) -
SELECT * FROM distances
ORDER BY
IF(_Sort = 1, uid, 0),
IF(_Sort = 2, NULL, 0),
IF(_Sort = 3, name, 0),
IF(_Sort = 4, NULL, 0),
...