Convert MySQL decimal values with a format that varies by value - mysql

I need to convert DECIMAL field to a CHAR representation, with a format that varies by the field's value.
Values are from a vendor's database; they will be:
> 0
values < 1 will be in tenths (e.g. 0.20)
values >= 1 will be integer values express as a decimal (e.g. 100.00)
Examples:
values < 1: 0.10, 0.20 to .1, .2
values >=1: 4101.00, 95.00 to 4101, 95
no commas if values > 999
Current approach:
CASE
WHEN id<1 THEN Cast(Round(item,1) AS Char)
ELSE Cast(Round(item,0) AS Char)
END item
** edit **
Second approach:
Trim(TRAILING '.' FROM Trim(BOTH '0' FROM Cast(item AS Char))) Item
Is there a more succinct way to do this?

Related

Add a digit to the end of an SQL result

I want the result to add number in the end in specific cases.
Like: if result is between 1 and 50, add 1.
If result is between 51 and 99, add 2 to the end.
If result is between 100 and 200, add 3 to the end.
Like:
Result = 25, do it 251.
Result 67, do it 672.
Result is 150, do it 1503.
I have created a table but the cases don't seem to work. How would I add a digit in specific cases?
CREATE TABLE Numbers(
Num INT
);
INSERT Numbers VALUES('12');
INSERT Numbers VALUES('112');
INSERT Numbers VALUES('12');
INSERT Numbers VALUES('122');
INSERT Numbers VALUES('1');
INSERT Numbers VALUES('2');
INSERT Numbers VALUES('12345678');
INSERT Numbers VALUES('12345');
SELECT * FROM Numbers;
SELECT RIGHT('15'+ CONVERT(VARCHAR,Num),6) AS NUM FROM Numbers;
SELECT LEFT(REPLICATE('0', 10) + CONVERT(VARCHAR, Num), 6) AS NUM FROM Numbers;
SELECT RIGHT('0' + CAST(Num AS VARCHAR(2)), 2) FROM Numbers
SELECT
CASE
WHEN Num BETWEEN 1 AND 99
THEN LEFT ('00' + CAST(Num AS VARCHAR(2)), 2)
ELSE
CAST(Num AS VARCHAR(10))
END
FROM Numbers
Since you're already using varchar on these values, I'd use concat - which simply mergs strings together. In this case you simply select what you want to merge, with what. Documentation on Concat() here.
Fiddle: https://www.db-fiddle.com/f/at2fqinuEao3b8coRSydTD/1
SELECT
CASE WHEN Num BETWEEN 1 AND 50
THEN concat(Num, '1')
WHEN Num BETWEEN 51 AND 99
THEN concat(Num, '2')
WHEN Num BETWEEN 100 AND 199
THEN concat(Num, '3')
ELSE Num END AS Num
FROM Numbers
In the examples of your 25,67 and 150 - this is the result:
Num
251
672
1503
You're working with numbers, so you can do Num*10 + 1 etc. Like this. fiddle
SELECT CASE WHEN Num BETWEEN 1 AND 50 THEN Num*10 + 1
WHEN Num BETWEEN 51 AND 99 THEN Num*10 + 2
WHEN Num BETWEEN 100 AND 199 THEN Num*10 + 3
ELSE Num END AS Num
FROM Numbers
That seems like it might be easier than string-casting and concatenating.
But you could do this if you really want strings. fiddle.
SELECT CASE WHEN Num BETWEEN 1 AND 50 THEN CONCAT(Num, '1')
WHEN Num BETWEEN 51 AND 99 THEN CONCAT(Num, '2')
WHEN Num BETWEEN 100 AND 199 THEN CONCAT(Num, '3')
ELSE CONVERT(Num, CHAR) END AS Num
FROM Numbers

How I can get limit_exceded value if value is less than or greater than in "limit_exceded" column i would get 1 else 0 in mysql

database table_img for better understanding
Pardon me for weak English writing
my sql query that i am getting value,low_value,high_value and limit_exceded_. issue is in limit_exceded to not get correct values, it shows all 0.
SELECT `myvalues`.`value`, `sub_types`.`low_value`, `sub_types`.`high_value`,
(case when myvalues.value > sub_types.low_value and myvalues.value < sub_types.high_value then 1 else 0 end) as limit_exceded
FROM `myvalues`
JOIN `sub_types` ON `myvalues`.`sub_type_id` = `sub_types`.`id`
WHERE `myvalues`.`sub_type_id` IN('68')
AND `myvalues`.`observation_id` IN('455', '471', '470', '469', '468', '467', '466', '465', '462', '461', '460', '459', '458', '457', '456', '372', '453', '373', '376', '439', '440', '441', '442', '443', '445', '446', '447', '448', '452', '454')
I want to get int 1 in front of those values whose value is less than 40 or greater than 180. Also it would it be appreciated if extract max and min from this list and count limit_exceded values
expected result is set value 1 where values is 900,9 and 1 etc
issue is in limit_exceded to not get correct values, it shows all 0
I suspect the issue is the types. The way the data lines up in the image and the use of strings in the WHERE clause suggest that the values are strings not numbers. A simply way to convert is to use + 0:
SELECT v.value, st.low_value, st.high_value,
(v.value + 0) > (st.low_value + 0) and (v.value + 0) < (st.high_value + 0) as limit_exceded
FROM myvalues v JOIN
sub_types st
ON v.sub_type_id = st.id
WHERE v.sub_type_id IN ('68') AND
v.observation_id IN ('455', '471', '470', '469', '468', '467', '466', '465', '462', '461', '460', '459', '458', '457', '456', '372', '453', '373', '376', '439', '440', '441', '442', '443', '445', '446', '447', '448', '452', '454')
Notes:
Table aliases make the query easier to write and to read.
Unnecessary backticks just make the query harder to write and to read.
In MySQL, you don't need the CASE expression -- you can just use the boolean expression.
That said, you should fix the data, not the query. If the values are numbers, store them as numbers, not string.

Is there a ROUNDDOWN() function in sql as there is in EXCEL

Say I have a table which has two columns i.e. Quantity and Percentages where my percentages are in decimals. Now I want to multiply these two columns and Round the value down to 2 decimals. Rounding down here means that all the numbers from 1-9 are rounded down. Is there an inbuilt function in SQL to do so as there is in Excel?
Examples:
13.567 should round to 13.56
136.7834 should round to 136.78
0.7699 should round to 0.76
I have tried searching for such a function online but couldn't come across an appropriate solution.
There's a FLOOR function, which can be adapted to your use case:
SELECT FLOOR(value * 100) / 100 AS RoundedValue
You can use TRUNCATE () for this rounddown
select TRUNCATE(2.847, 2) as rounddown
or
SELECT Floor(135.675); //for integer rounding, like 135
You can also use
select round(123.456, 2, 1) as rounddown
The 3rd parameter being non-zero will cause a truncation after the number of decimal points specified in the 2nd parameter.
DB Fiddle
https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/sqlref/src/tpc/db2z_bif_truncate.html
https://www.w3schools.com/sql/func_sqlserver_floor.asp
The solution to the problem is to truncate the extra decimal which can be achieved by using the extra parameter of the ROUND function which is ROUND(number, decimal_places, 0/1). Here if the last parameter is anything other than 0, it will truncate the rather than rounding off which is equivalent to the ROUNDDOWN() function of excel that I was looking for.
Alternatively, you can use the TRUNCATE() function, passing the number of decimal places to keep as the second parameter, which will drop off any extra decimals, acting as a ROUNDDOWN() function.
I hope this rounding utility helps somebody:
CREATE FUNCTION `get_round`(val DOUBLE, nDigits INT, RoundStyle VARCHAR(255)) RETURNS double
NO SQL
BEGIN
DECLARE a DOUBLE DEFAULT 0;
SET nDigits = ifnull(nDigits, 0);
CASE
WHEN UCASE(RoundStyle) IN ('ROUND NEAREST','', 'NEAREST', '', 'RND','ROUND', 'DEFAULT','DFLT', null) THEN #normal rounding, but up from 10.50#
SET a = round(val, nDigits);
WHEN UCASE(RoundStyle) IN('ROUND UP', 'UP') THEN #ROUND 10.554 to 10.56
SET a = ceil(val * (power(10, nDigits) )) / (power(10, nDigits));
WHEN UCASE(RoundStyle) IN('ROUND DOWN', 'DOWN') THEN #ROUND 10.555 to 10.55
SET a = truncate(val, nDigits) ;
WHEN UCASE(RoundStyle) IN('ROUND BANKER', 'BANKER','BANKERS ROUNDING') THEN #ROUND TO THE NEAREST EVEN 10.555 is 10.56 and 10.565 is 10.56
SET a = IF(ABS(val - TRUNCATE(val, nDigits)) * POWER(10, nDigits + 1) = 5
AND NOT CONVERT(TRUNCATE(ABS(val) * POWER(10, nDigits), 0), UNSIGNED) % 2 = 1,
TRUNCATE(val, nDigits), ROUND(val, nDigits));
WHEN UCASE(RoundStyle) IN('ROUND UP INTEGER', 'INT UP','UP INT') THEN #10.4 rounds to 11.0
SET a = ceiling(val);
WHEN UCASE(RoundStyle) IN('ROUND DOWN INTEGER', 'INT DOWN','DOWN INT') THEN #10.6 rounds to 10.0
SET a = floor(val);
END CASE;
RETURN ifnull(a, 0);
END
yes there are some Function in sql for round
ex:
SELECT ProductName, Price, FlOOR(Price) AS RoundedPrice
FROM Products;

how to update flag bit in mysql query?

This is my sql query,In flag(00000) every bit position have different specification, e.g. change 4th bit position to 1 when user is inactive.Here flag is varchar datatype(String).
$sql="select flag from user where id =1"
I got
flag=10001 #it may be flag="00001" or flag="00101"
I want to update 2nd bit of this flag to 1.
$sql="update user set flag='-1---' where id=1" #it may be flag='11001' or flag='01001' or flag='01110'
Actually,I want to to update 2nd bit of this flag to 1,but with out updating it like flag='11001'.I want to do some thing like this.
$sql="update user set flag='--change(flag,2bit,to1)--' where id =1" #this is wrong
What can I do for it , only using one sql query?Is it possible?
update user
set flag = lpad(conv((conv(flag, 2, 10) | 1 << 3), 10, 2), 5, '0')
where id = 1
conv(flag, 2, 10) converts the flag string from binary to decimal.
1 << 3 shifts a 1 bit 3 binary places to the left
| performs a binary OR of this, to set that bit. This arithmetic operation will automatically coerce the decimal string to a number; you can use an explicit CAST if you prefer.
conv(..., 10, 2) will convert the decimal string back to a binary string
lpad(..., 5, '0') adds leading zeroes to make the string 5 characters long
FIDDLE DEMO
To set the bit to 0, you use:
set flag = lpad(conv((conv(flag, 2, 10) & ~(1 << 3)), 10, 2), 5, '0')
you want to use the bitwise or operator |
update user set flag = flag | (1 << 1) where id =1
if flag was 101 flag will now be 111
if flag was 000 flag will now be 010
1 << 1 shifts 1 up one bit - making it 10 (binary 2)
edit - not tested but use
update user set flag = cast(cast(flag AS SIGNED) | (1 << 1) AS CHAR) where id =1
If you are going to use a VARCHAR, you are better off using string manipulation functions: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html
UPDATE user
SET flag = CONCAT(LEFT(flag, 1), '1', RIGHT(flag, 3))
WHERE id = 1
However, you probably want to convert this field to an INT so that you can use the bit functions: http://dev.mysql.com/doc/refman/5.0/en/bit-functions.html

MySQL order by string with numbers

I have strings such as M1 M3 M4 M14 M30 M40 etc (really any int 2-3 digits after a letter)
When I do " ORDER BY name " this returns:
M1, M14, M3, M30, M4, M40
When I want:
M1, M3, M4, M14, M30, M40
Its treating the whole thing as a string but I want to treat it as string + int
Any ideas?
You could use SUBSTR and CAST AS UNSIGNED/SIGNED within ORDER BY:
SELECT * FROM table_name ORDER BY
SUBSTR(col_name FROM 1 FOR 1),
CAST(SUBSTR(col_name FROM 2) AS UNSIGNED)
If there can be multiple characters at the beginning of the string, for example like 'M10', 'MTR10', 'ABCD50', 'JL8', etc..., you basically have to get the substring of the name from the first position of a number.
Unfortunately MySQL does not support that kind of REGEXP operation (only a boolean value is returned, not the actual match).
You can use this solution to emulate it:
SELECT name
FROM tbl
ORDER BY CASE WHEN ASCII(SUBSTRING(name,1)) BETWEEN 48 AND 57 THEN
CAST(name AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,2)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,1)
WHEN ASCII(SUBSTRING(name,3)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,2)
WHEN ASCII(SUBSTRING(name,4)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,3)
WHEN ASCII(SUBSTRING(name,5)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,4)
WHEN ASCII(SUBSTRING(name,6)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,5)
WHEN ASCII(SUBSTRING(name,7)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,6)
WHEN ASCII(SUBSTRING(name,8)) BETWEEN 48 AND 57 THEN
SUBSTRING(name,1,7)
END,
CASE WHEN ASCII(SUBSTRING(name,1)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,1) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,2)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,2) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,3)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,3) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,4)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,4) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,5)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,5) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,6)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,6) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,7)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,7) AS UNSIGNED)
WHEN ASCII(SUBSTRING(name,8)) BETWEEN 48 AND 57 THEN
CAST(SUBSTRING(name,8) AS UNSIGNED)
END
This will order by the character part of the string first, then the extracted number part of the string as long as there are <=7 characters at the beginning of the string. If you need more, you can just chain additional WHENs to the CASE statement.
I couldn't get this working for my issue which was sorting MLS Numbers like below:
V12345
V1000000
V92832
The problem was V1000000 wasn't being valued higher than the rest even though it's bigger.
Using this solved my problem:
ORDER BY CAST(SUBSTR(col_name FROM 2) AS UNSIGNED) DESC
Just removed the SUBSTR(col_name FROM 1 FOR 1)
You can use:
order by name,SUBSTRING(name,1,LENGTH(name)-1)
It split number and letters as separately.
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(
SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(col,'1', 1), '2', 1), '3', 1), '4', 1), '5', 1), '6', 1)
, '7', 1), '8', 1), '9', 1), '0', 1) as new_col
FROM table group by new_col;
Try remove the character with SUBSTR. Then use ABS to get the absolute value from field:
SELECT * FROM table ORDER BY ABS(SUBSTR(field,1));
Another method i used in my project is:
SELECT * FROM table_name ORDER BY LENGTH(col_name) DESC, col_name DESC LIMIT 1