Mysql query trivia - mysql

Updated: Please assume, I will have exactly 4 elements in my version number x.x.x.x
Table xyz (id int, os varchar, version varchar)
id os version
1 mac 1.0.0.4
2 android 1.0.1.2
3 mac 1.0.0.14
4 mac 1.0.0.07
I want to find the maximum version number of each operating system.
select max(version), os from xyz group by os
but in the above data sample, it returns 1.0.0.4 as the highest version of mac, rather than returning 1.0.0.14. Is there any way to fix the above query? I know version is varchar :( If there is NO solution possible, then I can change datatype to other, but that will still not solve the issue.

If the 4 parts in the version are all numbers and not bigger than 255, you can use the INET_ATON() function, and its reverse INET_NTOA():
SELECT INET_NTOA(MAX(INET_ATON(version))), os
FROM xyz
GROUP BY os ;
Tested in SQL-Fiddle

Mysql uses text sort for this field when you try to get max:
SELECT version FROM test ORDER BY VERSION;
1.0.0.14
1.0.0.4
1.0.07
1.0.1.2.4
But even if you try to cast this to integer, you will get
SELECT version FROM test ORDER BY CAST(VERSION AS SIGNED);
1
1
1
1
And this really has a sense. How do you want MySQL to guess, what you want?
MySQL should be used to store data, and you should format it yourself.
A fine solution would be to use several fields for version:
id int, os varchar, versionMajor int, versionMinor int, versionMoreMinor int, versionEvenMoreMinor int
You should be able to sort and to format them as you wish.

It is ugly, but it's doing the job:
select t1.os,t1.version from xyz t1 where (t1.os,
((cast(SUBSTRING_INDEX(t1.version, '.', 1) as unsigned)+1)*1000
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t1.version, '.', -3), '.', 1) as unsigned)+1)*100
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t1.version, '.', -2), '.', 1) as unsigned)+1)*10
+(cast(SUBSTRING_INDEX(t1.version, '.', -1) as unsigned))+1))
=(select t2.os,
max((cast(SUBSTRING_INDEX(t2.version, '.', 1) as unsigned)+1)*1000
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t2.version, '.', -3), '.', 1) as unsigned)+1)*100
+(cast(SUBSTRING_INDEX(SUBSTRING_INDEX(t2.version, '.', -2), '.', 1) as unsigned)+1)*10
+(cast(SUBSTRING_INDEX(t2.version, '.', -1) as unsigned))+1)
from xyz t2 where t2.os=t1.os);
sqlfiddle

What you'd really like to do here is use a SPLIT function, but that's not available in MySQL. However, you can write a user-defined function that will do the same thing, or refactoring this function code into a view.
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
Source.
Additionally, you may want to consider that the reason this is such a pain in the butt is because you're essentially violating first normal form. A version number of the form you specify is actually four distinct values: major version, minor version, revision (or maintenance), and build. Your table structure should use the same format:
Table xyz (id int not null, os varchar not null, version_major int not null, version_minor int not null, version_revision int null, version_build int null)
Then you just use an ORDER BY clause to sort the data. You can use a calculated field or a view to display the formatted version number.
Now, I know why you chose to use a single field. Version numbers are inconsistent, and many version numbers do not include revision or build values, other include letters, etc. You can change the int to a varchar and cover 90% of cases in my experience, however.

You may try to convert string to the integer and get MAX()
SELECT MAX( CONVERT( REPLACE( `version`, '.', '' ), UNSIGNED ) ) from xyz
upd
There are some another way with ending detection, but it doesn't work properly with minor version, maybe you will tune this heavy query to get it works well for you
SELECT `version`,
CONVERT(
CONCAT_WS( '.',
REPLACE( SUBSTRING( `version`, 1, ( CHAR_LENGTH( `version` ) - LOCATE( '.', REVERSE( `version` ) ) ) ), '.', '' ),
SUBSTRING( `version`, 1 + LOCATE( '.', REVERSE( `version` ) ) * -1 )
),
DECIMAL ( 10, 3 )
)
FROM `xyz`
sorry, code improved

Thanks to #ntvf for idea.
This will do what I am looking for.
select a.os, xyz.version from xyz,
(SELECT os, MAX( CONVERT(REPLACE(`version`,'.',''),UNSIGNED)) as version from xyz group by os) a
where
a.os=xyz.os and
CONVERT(REPLACE( xyz.version, '.', '' ), UNSIGNED) = a.version

Related

Convert String to integer and increment in MYSQL

I am new to MySQL and i am storing a version number in a column called version which is a string.I want to convert it into integer and increment by one.
For example if version = 2.2.1....By running a query i want to change it to 2.2.2
is there anyone who did similar queries?
You could use some string function
SELECT CONCAT(
LEFT(your_column, LENGTH(your_column) - LOCATE('.', REVERSE(your_column))+1),
CAST(SUBSTRING_INDEX(your_column, '.', -1) AS UNSIGNED) +1
)
eg
SELECT CONCAT(
LEFT("2.2.2", LENGTH("2.2.2") - LOCATE('.', REVERSE("2.2.2"))+1),
CAST(SUBSTRING_INDEX("2.2.2", '.', -1) AS UNSIGNED) +1
)

Sort MySQL results by a column with several values separated by a period

I suspect this may have already been asked, but I'm not sure how to phrase the question so that SO search engine picks it up.
I have a column called TCID, which contains values in this format:
1.A.1.1.1
4.A.1.1.1
2.B.1.1.10
2.B.1.1.2
...
There are 5 units in this TCID, separated by periods. I want the position to the left to take the highest priority, and then finally the last digit is the lowest priority.
So it would sort like this:
1.A.1.1.1
2.B.1.1.2
2.B.1.1.10
4.A.1.1.1
Here is the query I have so far. It almost works, but the last position is not getting sorted.
SELECT *
FROM system
WHERE cluster = \"$tc_name\"
ORDER BY CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',1) , 1 ) AS UNSIGNED),
SUBSTR( SUBSTRING_INDEX(tcid,'.',2) , LENGTH( SUBSTRING_INDEX(tcid,'.',1)) + 2 ),
CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',3) , LENGTH( SUBSTRING_INDEX(tcid,'.',2)) + 2 ) AS UNSIGNED),
CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',4) , LENGTH( SUBSTRING_INDEX(tcid,'.',3)) + 2 ) AS UNSIGNED)
Can anyone help me fix this or suggest a better way to do this?
There are obviously better ways to store this information in the database, such as storing the values in separate fields. However, it's not always possible to change the code base to do such things.
But I believe you just need to add the final order by to your query in order to for it to work as expected;
SELECT *
FROM system
WHERE cluster = "<some search term>"
ORDER BY CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',1) , 1 ) AS UNSIGNED),
SUBSTR( SUBSTRING_INDEX(tcid,'.',2) , LENGTH( SUBSTRING_INDEX(tcid,'.',1)) + 2 ),
CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',3) , LENGTH( SUBSTRING_INDEX(tcid,'.',2)) + 2 ) AS UNSIGNED),
CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',4) , LENGTH( SUBSTRING_INDEX(tcid,'.',3)) + 2 ) AS UNSIGNED),
CAST(SUBSTR( SUBSTRING_INDEX(tcid,'.',5) , LENGTH( SUBSTRING_INDEX(tcid,'.',4)) + 2 ) AS UNSIGNED);
Please see this sqlfiddle to check
Just for fun...
SELECT *
FROM
(
SELECT '1.A.1.1.1' x
UNION ALL
SELECT '4.A.1.1.1'
UNION ALL
SELECT '2.B.1.1.10'
UNION ALL
SELECT '2.B.1.1.2'
) a
ORDER BY
INET_ATON(CONCAT(MID(x,1,1),MID(x,4,1000)));

MySQL's LIKE operator doesn't find phone number that matches exactly

this is my first question here, having a strange problem with MySQL:
I have phone numbers in a database field 'phone' of type varchar(50) utf8mb4_unicode_ci. Type is varchar, because the numbers can have delimiters like / or - and spaces for formatting.
If I select the phone number with:
SELECT phone, REPLACE(REPLACE(REPLACE(REPLACE(phone,' ',''),'-',''),'/',''),'.','') FROM [table] WHERE id=405760
the output is:
0151 / ‎434 77 588, 0151‎43477588
So the REPLACE part does work, also, when I select the
But searching after those phone numbers with the LIKE operator fails for this specific phone number 015143477588 (others work perfectly):
SELECT *
FROM [table]
WHERE REPLACE(REPLACE(REPLACE(REPLACE(phone,' ',''),'-',''),'/',''),'.','') like '%015143477588%'
If I use the = instead LIKE it works.
SELECT *
FROM [table]
WHERE REPLACE(REPLACE(REPLACE(REPLACE(phone,' ',''),'-',''),'/',''),'.','') = '015143477588'
as said, other phone numbers work....any ideas?
I have checked your query in my database it is working fine
SELECT *
FROM users
WHERE REPLACE( REPLACE( REPLACE( REPLACE( phone, ' ', '' ) , '-', '' )
, '/', '' ) , '.', '' )
LIKE '%015143477588%'
You can check your query after change Collation from utf8mb4_unicode_ci to utf8_swedish_ci

mySQL extract number or string depending on value

Please take a look at a table below:
I need "the last part" of "what I have" to be number if it's all number and string if it contains character.
In Excel I've achieved this with the following function (as shown above):
=IFERROR(VALUE(TEXT(D2;"0"));TEXT(D2;"0"))
However I want to do this in mySQL in order to compute more effectively.
I've "floated" somwhere around CASE with CAST or CONVERT and also TRIM functions, but I haven't been able to put up something sensical.
A nice "bonus" would be to extract "the part part" by looking for the last "" character (so first "" from right of the string) but no idea at all how to achieve that.
Use SUBSTRING_INDEX.
Query
SELECT
CASE
WHEN SUBSTRING_INDEX(records.data, '_', -1) > 1 # is int check '00004949' returns 1
THEN SUBSTRING_INDEX(records.data, '_', -1) + 0 # converts '00004949' to 4949
ELSE SUBSTRING_INDEX(records.data, '_', -1)
END
AS word
FROM (
SELECT "TRA_PL_NWL_EMA_NWLY_DAI_000_20170610_IN1_01P002bc" AS DATA
UNION
SELECT "TRA_PL_NWL_EMA_NWLY_DAI_000_2017_0909_JET_00004949" AS DATA
) records
Result
word
----------
01P002bc
4949
Following query will somewhat achieve the task:
SELECT
case
when SUBSTRING_INDEX(value, "_", -1) REGEXP('(^[0-9]+$)')
then Trim(Leading 0 from SUBSTRING_INDEX(value, "_", -1))
else SUBSTRING_INDEX(value, "_", -1)
end as Value
From yourtable;
Click here for Demo
Hope it helps!
I hope this is what u want.
At least it does what you have asked for.
SELECT
CASE
WHEN (
CONVERT(
substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
, signed integer
)
) = 0
THEN substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
ELSE CONVERT(
substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
, signed integer
)
END as NUMBER
from test.test
This is my test Table and result of SQL:
txt NUMBER
DA_DA_ADAD_ADAD_ADAD_asd123 asd123
DA_DA_ADAD_ADAD_ADAD_000123 123
DA_DA_ADAD_ADAD_ADAD_444 444
DA_DA_ADAD_ADAD_ADAD_bsd123 bsd123
DA_DA_ADAD_ADAD_ADAD_csd123 csd123
DA_DA_ADAD_ADAD_ADAD_dsd123 dsd123

Sorting/ordering in MySQL

I'm having a little problem with trying to sort the contents of a table programs by the column prog_id which holds the id of each program in the following format:
prog_id
1.0.1, 1.0.2, 1.0.3, ..., 1.0.10, 1.0.11, ..., 1.1.0, 1.1.1 etc
When I sort by prog_id i get
1.0.1, 1.0.10, 1.0.11, 1.0.2, 1.0.3 ...
which is correct as far as MySQL goes but not correct for the order in which the data should display.
I tried using another column, orderby in which I could save an index and order by that but I would have to enter the values manually and there are a few thousand rows in my table which would take quite a long time to do.
Any tricks I could use to get my data to display in the "proper" order? BTW, I'm using PHP & MySQL.
You could split them into their constituent parts like:
SELECT REPLACE(SUBSTRING(SUBSTRING_INDEX(prog_id, '.', 1),
LENGTH(SUBSTRING_INDEX(prog_id, '.', 1 -1)) + 1),
'.', '') AS id1,
REPLACE(SUBSTRING(SUBSTRING_INDEX(prog_id, '.', 2),
LENGTH(SUBSTRING_INDEX(prog_id, '.', 2 -1)) + 1),
'.', '') AS id2,
REPLACE(SUBSTRING(SUBSTRING_INDEX(prog_id, '.', 3),
LENGTH(SUBSTRING_INDEX(prog_id, '.', 3 -1)) + 1),
'.', '') AS id3
FROM programs
ORDER BY CAST(id1 AS INT(4)), CAST(id2 AS INT(4)), CAST(id3 AS INT(4))
The best method would be to create the the extra fields like yoda2k says, but if you don't have that access then you could use the above.
You could encapsulate that into a function like:
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
Then do:
SELECT SPLIT_STR(prog_id, '.', 1) AS id1,
SPLIT_STR(prog_id, '.', 2) AS id2,
SPLIT_STR(prog_id, '.', 3) AS id3,
FROM programs
ORDER BY CAST(id1 AS INT(4)), CAST(id2 AS INT(4)), CAST(id3 AS INT(4))
Not optimal solution -
...ORDER BY substring_index(prog_id, '.', 1), substring_index(substring_index(prog_id, '.', 2), '.', -1), substring_index(prog_id, '.', -1)
Odd solution, but try it -
...ORDER BY INET_ATON(prog_id)
You could use 3 fields e.g. major_version, minor_version, build_number, make them integer fields and use mysqls buildin "ORDER BY major_version,minor_version,build_number" which will order the fields in the desired way.