mysql sort by number with dots - mysql

i have a column with name title with rows :
1.8.8
1.8.9
1.9.1
1.9.2
1.8.10
and I need sort like this
1.8.8
1.8.9
1.8.10
1.9.1
1.9.2
is any way how to make it? (type of column is varchar)

Clunky, but should work provided all of your entries are in the format x.x.x:
select yourColumn
from yourTable
order by
cast(substring_index(yourColumn,'.',1) as unsigned),
cast(substring_index(substring_index(yourColumn,'.',2),'.',-1) as unsigned),
cast(substring_index(substring_index(yourColumn,'.',3),'.',-1) as unsigned)
;

DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(legal VARCHAR(12) NOT NULL PRIMARY KEY);
INSERT INTO my_table VALUES('1.8.8'),('1.8.9'),('1.9.1'),('1.9.2'),('1.8.10');
SELECT * FROM my_table ORDER BY INET_ATON(legal);
+--------+
| legal |
+--------+
| 1.8.8 |
| 1.8.9 |
| 1.8.10 |
| 1.9.1 |
| 1.9.2 |
+--------+
Note that this is a hack. In consequence, it has certain limitations. That said, there's a high likelihood that it will work fine for your purposes.

It may help, Simple and Exact Answer:
SELECT `COLUMN_NAME` FROM `TABLENAME` WHERE 1 ORDER BY `COLUMN_NAME` *1

This is called "natural sort" and there is no way to perform natural sort in MySQL: Natural Sort in MySQL
You can solve it by using special sorting field. With values like:
10808
10809
10901
10902
10910
(leading zeros)
PS as a bonus - sorting integers works muuuuch faster than sorting strings (especially strings with some magic rules). Especially if you create sorting index.

none of the answer quite were practical(either wrong or not performant).
Let's think your varchar column with dots is productId then you must use:
SELECT * FROM PRODUCT_TABLE ORDER BY productId * 1,length(productId)
productId * 1 gives you the same result as INET_ATON(productId) and they both are incomplete.
0.0.0
0.0
0
I got a result like that so to fix that just added length(productId).
0
0.0
0.0.0
and now everything is fine.

Related

mysql int column matching against a string

I have the following query and result:
mysql> SELECT item_id FROM phppos_items WHERE item_id = '5CG4500RRL';
+---------+
| item_id |
+---------+
| 5 |
+---------+
item_id is an int(11) primary key
How do I prevent this from matching? It looks like it is somehow becoming 5 when matching.
I still want to run this code so I don't have to change a lot of logic so I would prefer to keep it in mysql to do a strict comparison if possible.
I can be done by several methods. For example:
SELECT item_id
FROM phppos_items
WHERE '5CG4500RRL' REGEXP '^[0-9]+$' AND item_id = '5CG4500RRL';
Here we check is input value digits only and it equal to item_id.
Here you can find more options to check input value.

LPAD without trimming in mysql

I am trying to find a way to add 0 at the beginning of an ID without trimming it :
I am currently using LPAD(id,4,"0") witch works fine until 9999
Request : SELECT LPAD(12345,4,"0");
Excepted result : 12345
Result : 1234
I am Looking for a Request who does LPAD(id, MAX(LENGTH(id),4), "0")
I found SELECT IF(LENGTH(12345)>4, 12345, LPAD(12345, 4, "0")); but i would prefer if the 4 was in a single place (to make it easier use it elsewhere).
Is there a build-in function that does what i'm looking For ?
Or is there an alternative to the function MAX() that would work in that situation ?
Or should I stick with the IF(LENGTH) solution and it's drawbacks ?
Edit :
ZEROFILL doesn't fit my needs because I also need the id without 0s.
I mainly use the ID without the LPAD(), but when I do, I use it with a prefix : CONCAT("PFX", LPAD(id,4,"0"))
Thanks for your Help
PS: please tell me if i do anything wrong, it's my first time asking here.
Well I had similar problem with LPAD, it was truncating number to its pad length. According to https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_lpad it is expected result.
As far as I can see, nobody mentioned answer that solved my same problem:
LPAD(id, GREATEST(LENGTH(id), 4), "0")
It works as expected. Pads any id's shorter than 4 characters with 0, and returns unchanged id's that are longer than 4 characters.
I'm leaving my answer here for other people, that will find this question in the future.
You can cat the length of a int field and also set zerofill like this:
CREATE TABLE `num` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`Numbers` int(5) unsigned zerofill DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Insert values:
INSERT INTO `num` (`id`, `Numbers`)
VALUES
(1, 1),
(2, 22),
(3, 123456);
See the result:
MariaDB []> select * from num;
+----+---------+
| id | Numbers |
+----+---------+
| 1 | 00001 |
| 2 | 00022 |
| 3 | 123456 |
+----+---------+
3 rows in set (0.000 sec)
MariaDB []>
without zerofill you can calc it like:
SELECT
id,
SUBSTRING( CONCAT('000000',Numbers),- GREATEST(LENGTH(Numbers),5))
FROM num;
Result:
MariaDB []> SELECT
-> id,
-> SUBSTRING( CONCAT('000000',Numbers),- GREATEST(LENGTH(Numbers),5))
-> FROM num;
+----+--------------------------------------------------------------------+
| id | SUBSTRING( CONCAT('000000',Numbers),- GREATEST(LENGTH(Numbers),5)) |
+----+--------------------------------------------------------------------+
| 1 | 00001 |
| 2 | 00022 |
| 3 | 123456 |
+----+--------------------------------------------------------------------+
3 rows in set (0.001 sec)
MariaDB []>
Ask yourself, why a number would need leading zeroes at all. No number in practical and theoretical math needs leading zeroes.
Maybe you want to visuals something, and not work with the result as a real number.
In that case, you can either declare the field in the table ZEROFILL and select it converted to char: SELECT CONVERT(id, CHAR);.
Other than that, the only way is a mix of CONCAT() and LENGTH(), that way you avoid stripping the ID to the value in LPAD().
LPAD
You can achieve this with an REGEXP_REPLACE to do a LPAD equivalent but without truncating the result. If you want a minimum length of 4 and padding with 0, you can do:
REGEXP_REPLACE(CONCAT('0000', id), '^0{0,4}(.{4,})$', '\\1')
You can adapt this by replacing '0000' with REPEAT('0', 4) to do it programmatically. The main advantage in this approach, is to not repeating the padded thing. In my use cases, I can have very long and complex things that I don't want to duplicate to only pad them, so this code fragment do its job.
RPAD
For those who have the problem for RPAD, you need a very close pattern:
REGEXP_REPLACE(CONCAT(id, '0000'), '^(.{4,}?)0{0,4}$', '\\1')
The two blocks in the pattern are essentially inverted but you can see .{4,}? instead of .{4,}. It's for set the greediness to lazy for the {4,} quantifier. It's needed here.

Sorting order behaviour between Postgres and Mysql

I have faced some strange sort order behaviour between Postgres & mysql.
For example, i have created simple table with varchar column and inserted two records as below in both Postgres and Mysql.
create table mytable(name varchar(100));
insert into mytable values ('aaaa'), ('aa_a');
Now, i have executed simple select query with order by column.
Postgres sort order:
test=# select * from mytable order by (name) asc;
name
------
aa_a
aaaa
(2 rows)
Mysql sort order:
mysql> select * from mytable order by name asc;
+------+
| name |
+------+
| aaaa |
| aa_a |
+------+
2 rows in set (0.00 sec)
Postgres and mysql both returning same records with different order.
My question is which one correct?
How to get results in same order in both database?
Edited:
I tried with query with ORDER BY COLLATE, it solved my problem.
Tried like this
mysql> select * from t order by name COLLATE utf8_bin;
+------+
| name |
+------+
| aa_a |
| aaaa |
+------+
3 rows in set (0.00 sec)
Thanks.
There is no "correct" way to sort data.
You need to read up on "locales".
Different locales will provide (among other things) different sort orders. You might have a database using ISO-8859-1 or UTF-8 which can represent several different languages. Rules for sorting English will be different for those from French or German.
PostgreSQL uses the underlying operating-system's support for locales, and not all locales are available on all platforms. The alternative is to provide your own support, but then you can have incompatibilities within one machine.
I believe MySQL takes the second option, but I'm no expert on MySQL.

MySQL ORDER BY Alphanumeric values, first all integers, then all alphas

searched around awhile for a solution to this problem, but no answer yet.
Have a column of alphanumeric model ID numbers to populate an index in a certain order. Two different attempts with the order they produced:
ORDER BY Model_ID ASC
1278-8
211E
350-50
996
3800P
AP23
AP263
AP26M
JA042
ORDER BY CAST(Model_ID AS UNSIGNED), Model_ID
AP23
AP263
AP26M
JA042
211E
350-50
996
1278-8
3800P
However, I need to have it sorted like so, with all of the integer-starting numbers exhausted first:
211E
350-50
996
1278-8
3800P
AP23
AP263
AP26M
JA042
Help? Thanks
For the sample data, this will get the desired order:
ORDER BY Model_ID+0=0, Model_ID+0, Model_ID ASC
Let's unpack that a bit.
The expression Model_ID+0 evaluates Model_ID in a numeric context, by adding zero to it. Basically, MySQL gets whatever leading characters that can be converted to a number, and disregards the rest. For values that can't be interpreted as a number, MySQL returns 0.
The first expression checks if the numeric value is zero, so those will be sorted last. (MySQL returns numeric value of 1 for boolean TRUE, 0 for FALSE.)
The second expression gets the non-zero values sorted in numeric order.
NOTE: these expressions "work" for the sample data; the edge case is values that have leading zeros as the numeric value, e.g. '000ABC' will be sorted after all the other "numeric" values. And MySQL doesn't stop at just the integer portion, if there's a dot character, that can be interpreted as a decimal point.
You can see the values of the expressions (for testing), by including them in the SELECT list, e.g.
SELECT Model_ID+0=0, Model_ID+0, ...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table (model_id VARCHAR(20) NOT NULL PRIMARY KEY);
INSERT INTO my_table VALUES
('1278-8'),
('211E'),
('350-50'),
('996'),
('3800P'),
('AP23'),
('AP263'),
('AP26M'),
('JA042');
SELECT model_id FROM my_table ORDER BY model_id + 0 = 0,model_id + 0;
+----------+
| model_id |
+----------+
| 211E |
| 350-50 |
| 996 |
| 1278-8 |
| 3800P |
| AP23 |
| AP263 |
| AP26M |
| JA042 |
+----------+

How can I use the LIKE operator on a list of strings to compare?

I have a query I need to run on almost 2000 strings where it would be very helpful to be able to do a list like you can with the "IN" operator but using the LIKE comparison operation.
For example I want to check to see if pet_name is like any of these (but not exact): barfy, max, whiskers, champ, big-D, Big D, Sally
Using like it wouldn't be case sensitive and it can also have an underscore instead of a dash. Or a space. It will be a huge pain in the ass to write a large series of OR operators. I am running this on MySQL 5.1.
In my particular case I am looking for file names where the differences are usually a dash or an underscore where the opposite would be.
For this task I would suggest making use of RegExp capabilities in MySQL like this:
select * from EMP where name RLIKE 'jo|ith|der';
This is case insensitive match and will save from multiple like / OR conditions.
You could do something like this -
SELECT FIND_IN_SET(
'bigD',
REPLACE(REPLACE('barfy,max,whiskers,champ,big-D,Big D,Sally', '-', ''), ' ', '')
) has_petname;
+-------------+
| has_petname |
+-------------+
| 5 |
+-------------+
It will give a non-zero value (>0) if there is a pet_name we are looking for.
But I'd suggest you to create a table petnames and use SOUNDS LIKE function to compare names, in this case 'bigD' will be equal to 'big-D', e.g.:
SELECT 'bigD' SOUNDS LIKE 'big-D';
+---------------------------+
| 'bigD'SOUNDS LIKE 'big-D' |
+---------------------------+
| 1 |
+---------------------------+
Example:
CREATE TABLE petnames(name VARCHAR(40));
INSERT INTO petnames VALUES
('barfy'),('max'),('whiskers'),('champ'),('big-D'),('Big D'),('Sally');
SELECT name FROM petnames WHERE 'bigD' SOUNDS LIKE name;
+-------+
| name |
+-------+
| big-D |
| Big D |
+-------+
As first step put all static values in any temporary table, this would be lookup dictionary.
SELECT * FROM Table t
WHERE EXISTS (
SELECT *
FROM LookupTable l
WHERE t.PetName LIKE '%' + l.Value + '%'
)
Configure the column containing those 2000 values for full-text searching. Then you can use MySQL's full-text search feature. Refer to their docs
You could use REGEXP instead. It worked like a charm for me
pet_name regexp 'barfy|max|whiskers|champ|you name it'