How to natural sort “X-Y” string data, first by X and then by Y? - mysql

Given this data:
W18-40461
W19-1040
W20-4617
W20-100
I've tried several of the common natural sorting methods for mysql, but they won't sort these in a natural descending way, like:
W20-4617
W20-100
W19-1040
W18-40461
For example:
select theID
from Table
where theID
order by lpad(theID, 9, 0) desc

Assuming the parts on either side of the - are limited to 2 digits and 5 digits respectively, you can extract the two numeric values using SUBSTR (and LOCATE to find the - between the two numbers) and then LPAD to pad each of those values out to 2 and 5 digits to allow them to be sorted numerically:
SELECT *
FROM data
ORDER BY LPAD(SUBSTR(id, 2, LOCATE('-', id) - 2), 2, '0') DESC,
LPAD(SUBSTR(id, LOCATE('-', id) + 1), 5, '0') DESC
Output (for my expanded sample):
id
W20-12457
W20-4617
W20-100
W19-1040
W18-40461
W4-2017
Demo on db-fiddle
If the values can have more than 2 or 5 digits respectively, just change the second parameters to LPAD to suit.

I would do this as:
order by substring_index(col, '-', 1) desc,
substring_index(col, '-', -1) + 0 desc
This orders by the part before the hyphen as a string. And it converts the part after the hyphen to a number for sorting purposes.

Related

How to get the smallest number in a series of items, for each series?

I have two tables in my database, one with projects and another with tickets. I want the first ticket for each project.
Table project has:
AAA
BBB
CCC
Table tickets has:
AAA-1
AAA-2
BBB-2
BBB-3
CCC-100
CCC-101
What I want is a list of the smallest ticket in each project so the result should be AAA-1, BBB-2, CCC-101. Is this possible with sql alone? Do I need a function?
I have been trying with min and joins but I am not able to mix them properly...
Thanks
Possibly the simplest method is:
select concat(substring_index(ticket, '-', 1), , '-',
min(substring_index(ticket, '-', -1) + 0)
) as first_ticket
from t
group by substring_index(ticket, '-', 1)
This reconstructs the value after taking the min() as a number.
If you don't want to reconstruct the value:
select (select t.ticket
from tickets t
where t.ticket like concat(p.project, '-%')
order by length(ticket), ticket
limit 1
) as first_ticket
from projects p;
This uses just string operations for the minimum value, so it assumes that there is no zero-padding. It could be modified to use numbers as well:
order by p.project, substring_index(ticket, '-', -1) + 0

Sql order string with numbers asc

I have strings in my db like this:
DE-1016-860
DE-1016-1078
DE-1016-1166
How can I ORDER BY order_numbers this elements in a SELECT like this:
DE-1016-1166
DE-1016-1078
DE-1016-860
Found a solution:
ORDER BY SUBSTR(order_number FROM 1 FOR 8), CAST(SUBSTR(order_number FROM 8) AS UNSIGNED)
First I gave them the value 1 for the startpoint and 8 for the endpoint:
12345678
DE-1016-
You can see the eight characters. Second I make a cast from the eighth number and it works fine. It give me my numbers sorted by by the highest at the top to the smallest at the end.
If your format is always AA-####-.........
then you can try using LEFT(), RIGHT(), and SUBSTRING().
For example:
ORDER BY LEFT(order_number,2), SUBSTRING(order_number,4,4) DESC, SUBSTRING(order_number, 9,4) DESC
This assumes your prefix is always the same, thus to_number the last characters:
SELECT str
FROM (SELECT 'DE-1016-860' str FROM DUAL
UNION ALL
SELECT 'DE-1016-1078' str FROM DUAL
UNION ALL
SELECT 'DE-1016-1166' str FROM DUAL)
ORDER BY TO_NUMBER (SUBSTR (str, 9, 4)) DESC

sql new column by delimiter with order ID

Hi this maybe a simple one but I need help specifically for MYsql
I have the data in one column lets call the column WORK 1,2,3,5,2 (these values are sometimes longer and shorter or more values are present e.g 12,15,11,15,16,143)
I need these to be put into 1 new column for each delimiter and have an ID for the order presented. e.g output
SELECT
*
FROM (SELECT
ROW_NUMBER()
OVER (ORDER BY WORK) AS Row,
RIGHT(LEFT(T.WORK, Number - 1),
CHARINDEX(',', REVERSE(LEFT(',' + T.WORK, Number - 1)))) AS a
FROM master..spt_values,
<YOUR_TABLENAME> T
WHERE Type = 'P'
AND Number BETWEEN 1 AND LEN(T.WORK) + 1
AND (SUBSTRING(T.WORK, Number, 1) = ','
OR SUBSTRING(T.WORK, Number, 1) = '')) AS A

Mysql - count values from comma-separated field [duplicate]

This question already has answers here:
How to count items in comma separated list MySQL
(6 answers)
Closed last year.
I have to do some statics from read-only db where value are stored in a weird form
example:
I have 2 rows like
ID text field
1 1001,1003,1004
2 1003, 1005
I need to be able to count that this is "5".
I don't have write access so don't know how to read and count right away without creation a function or something like that.
Clever solution here on SO: How to count items in comma separated list MySQL
LENGTH(textfield) - LENGTH(REPLACE(textfield, ',', '')) + 1
EDIT
Yes you can select it as an additional column: and correcting with the CHAR_LENGTH from #HamletHakobyan's answer:
SELECT
ID,
textfield,
(CHAR_LENGTH(textfield) - CHAR_LENGTH(REPLACE(textfield, ',', '')) + 1) as total
FROM table
SELECT SUM(LENGTH(textfield) - LENGTH(REPLACE(textfield, ',', '')) + 1)
FROM tablename
There is a small but significant omission in all answers. All will work only if database character set is utf8 or so, i.e. where symbol , gets one byte. The fact that the LENGTH function returns number of bytes instead of chars. Right answer is to use CHAR_LENGTH which returns number of characters.
SELECT
SUM(CHAR_LENGTH(textfield) - CHAR_LENGTH(REPLACE(textfield, ',', '')) + 1) cnt
FROM yourTable
You could use something like this:
select sum(total) TotalWords
from
(
select length(`text field`) - length(replace(`text field`, ',', '')) + 1 total
from yourtable
) x
See SQL Fiddle with Demo
SELECT (LENGTH(column_name) - LENGTH(REPLACE(column_name, ',', '')) + 1) as value_count
FROM table_name
Here LENGTH(column_name) - LENGTH(REPLACE(column_name, ',', '')) gives the number of commas in the value of each column. And +1 with this value provides the number of values separated by comma.
All is wrong and doesn't works for me.
The only one that work is this bellow
SELECT (length(`textfield`) - length(replace(`textfield`, ',', '')) + 1) as my
FROM yourtable;
This is my fiddle
http://sqlfiddle.com/#!9/d5a8e1/10
If someone looking for a solution to return 0 for empty fields.
IF(LENGTH(column_name) > 0, LENGTH(column_name) - LENGTH(REPLACE(column_name, ',', '')) + 1, 0)

mysql sorting of version numbers

I have values like:
1.1.2
9.1
2.2
4
1.2.3.4
3.2.14
3.2.1.4.2
.....
I need to sort those values using mysql. The data type for this one is varbinary(300).
The desired output will be like:
1.1.2
1.2.3.4
2.2
3.2.1.4.2
3.2.14
4
9.1
The Query is:
select version_number from table order by version_number asc
it does not give the correct sorting order.
The desired output of this is:
1.1.2
1.2.3.4
2.2
3.2.1.4.2
3.2.14
4
9.1
The version numbers are up to 20 digits (like 1.2.3.4.5.6.7.8.9.2.34) and more also. There is no particular max size and the standard version is just like above mentioned.
Try abusing the INET_ATON function to do the sorting like so:
SELECT version_number FROM table ORDER BY INET_ATON(SUBSTRING_INDEX(CONCAT(version_number,'.0.0.0'),'.',4))
This trick was originally posted on the mysql mailing list, so many thanks to the original poster, Michael Stassen!
Here's what he had to say:
If each part is no larger than 255, you can leverage INET_ATON() to do
what you want (up to the 4th part). The trick is making each of these
look like an IP first by using CONCAT to add '0.0.0' to make sure every
row has at least 4 parts, then SUBSTRING_INDEX to pull out just the
first 4 parts.
Now, I must point out that because we are sorting on a function of the
column, rather than on the column itself, we cannot use an index on the
column to help with the sort. In other words, the sorting will be
relatively slow.
In the latter case, he recommends a solution similar to the one posted by #spanky (separate columns).
I would store it in three separate columns, one for each part of the version number.
Make each column a TINYINT and even create an index across the 3 columns. That should make things simple.
Then you can do:
select CONCAT(v1,'.',v2,'.',v3) AS version_number FROM table ORDER BY v1 asc, v2 asc, v3 asc
If you'd like to support versions like 1.1-beta or using old MySql versions without INTE_ATON, you can get the same sort by splitting the version and sorting each part as an integer and string:
SELECT
version,
REPLACE(SUBSTRING(SUBSTRING_INDEX(version, '.', 1), LENGTH(SUBSTRING_INDEX(version, '.', 1 - 1)) + 1), '.', '') v1,
REPLACE(SUBSTRING(SUBSTRING_INDEX(version, '.', 2), LENGTH(SUBSTRING_INDEX(version, '.', 2 - 1)) + 1), '.', '') v2,
REPLACE(SUBSTRING(SUBSTRING_INDEX(version, '.', 3), LENGTH(SUBSTRING_INDEX(version, '.', 3 - 1)) + 1), '.', '') v3,
REPLACE(SUBSTRING(SUBSTRING_INDEX(version, '.', 4), LENGTH(SUBSTRING_INDEX(version, '.', 4 - 1)) + 1), '.', '') v4
FROM
versions_table
ORDER BY
0+v1, v1 DESC, 0+v2, v2 DESC, 0+v3, v3 DESC, 0+v4, v4 DESC;
Use regular expressions. First normalize the value:
SELECT REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE('v1.22.333', '^v', ''),
'(^|\\.)(\\d+)',
'\\100000\\2'
),
'0+(\\d{5})(\\.|$)',
'\\1\\2'
)
Output:
00001.00022.00333
Then you can sort normally.
This solution works with any number of components. You can scale component length from 5 to any fixed length.