Naturally ORDER a column containing hierarchical item names - mysql

I have a column (of VARCHAR type) with values like these:
1
2
3
1.1
1.1.1
1.2.1
5
4
7
8
9
10
10.2
10.1
I hop[e to select this column and order it naturally, like the following:
1
1.1
1.1.1
1.2.1
2
3
4
5
...
I have tried ordering it with this for example and a lot of other query
SELECT data
FROM sample
ORDER BY LEN(data), data
Does someone have an idea how to do this?

try this
ORDER BY data, LEN(data)
or this
ORDER BY CONVERT(SUBSTRING_INDEX(data, ',', -1), SIGNED), Len(data)
i give demo in mysql as tsql there is not in sqfiddle .
DEMO

You seem to be hoping to order a series of hierarchically named items in a natural order. It looks like these items' names take the form.
token [ .token [. token [ .token ]]]
where subsequent tokens after the first are optional.
I suppose you want each token, if it's numeric, to be handed as a number. That is, for example, you want 1.123 to come after 1.2 because 123 is numerically greater than 2.
You didn't say what you want done with alphabetical tokens, e.g. 401.k and 403.b. I suppose they should come after the numerical ones, but in lexical order.
This query (http://sqlfiddle.com/#!2/81756/2/0) will do the trick out to five hierarchical levels of tokens.
SELECT col
FROM T
ORDER BY
FLOOR(SUBSTRING_INDEX(col,'.',1)),
SUBSTRING_INDEX(col,'.',1),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',1)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',1))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',2)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',2))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',3)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',3))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',4)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',4)))
Why does this work? FLOOR() converts the leftmost part of a string to an integer, so it picks up the leading integer. If it doesn't find any numbers in the string it's trying to convert, it returns zero.
And, SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',NNN))) picks up the part of the col item to the right of the NNNth dot.

The data seems something like Hierarchical. With the current set of data supplied, if data is converted to hierarchical Data the order by can be done using something similar to:-
SELECT data FROM sample ORDER BY CAST ( '/' + replace( data, '.',
'/' ) + '/' as hierarchyid )

Related

MYSQL - Find rows, where part of search string matches part of value in column

I wasn't able to find this anywhere, here's my problem:
I have a string like '1 2 3 4 5' and then I have a mysql table that has a column, let's call it numbers, that look like this:
numbers
1 2 6 8 9 14
3
1 5 3 6 9
7 8 9 23 44
10
I am trying to find the easiest way (hopefully in a single query) to find the rows, where any of the numbers in my search string (1 or 2 or 3 or 4 or 5) is contained in the numbers column. In the give example I am looking for rows with 1,2 and 3 (since they share numbers with my search string).
I am trying to do this with a single query and no loops.
Thanks!
The best solution would be to get rid of the column containing a list of values, and use a schema where each value is in its own row. Then you can use WHERE number IN (1, 2, 3, 4, 5) and join this with the table containing the rest of the data.
But if you can't change the schema, you can use a regular expression.
SELECT *
FROM yourTable
WHERE numbers REGEXP '[[:<:]](1|2|3|4|5)[[:<:]]'
[[:<:]] and [[:<:]] match the beginning and end of words.
Note that this type of search will be very slow if the table is large, because it's not feasible to index it.
Here is a start point (split string function) : http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/ := SplitString(string,delimiter,position)
Create a function so it converts a string to an array := stringSplitted(string,delimiter)
Create a function so it compares two arrays :=arrayIntersect(array1, array2)
SELECT numbers
FROM table
WHERE arrayIntersect(#argument, numbers)
Two function definitions with loops and one single query without any loop
SELECT * FROM MyTable WHERE (numbers LIKE '%1%' OR numbers LIKE '%2%')
or you can also use REGEX something like this
SELECT * FROM events WHERE id REGEXP '5587$'

MySQL sorting with alphanumeric prefix

I've got a database with a column that contains the following data:
aaa-1
aaa-2
aaa-3
...
aaa-10
aaa-11
...
aaa-100
aaa-101
...
aaa-1000
When I query and sort the data in ascending order, I get:
aaa-1
aaa-10
aaa-11
...
aaa-100
aaa-101
...
aaa-1000
...
aaa-2
...
aaa-3
Is this actually the correct (machine) way of sorting? Is the order being screwed up because of the aaa- prefix? How do I go about sorting this the way a human would (ie something that looks like the first snippet)?
P.S. If the problem does lie in the prefix, is there a way to remove it and sort with just the numeric component?
P.P.S. It's been suggested to me that I should just change my data and add leading zeroes like aaa-0001 and aaa-0002, etc. However, I'm loathe to go that method as each time the list goes up an order of 10, I'd have to reformat this column.
Thank you all in advance! :)
You can extract the number part, convert it to numeric data type and then do an ORDER BY:
SELECT mytable.*,
CAST(SUBSTRING_INDEX(mycolumn, '-', - 1) AS UNSIGNED) mycolumnintdata
FROM
mytable
ORDER BY mycolumnintdata;
If there are expressions which does not match number, the CAST function would return 0 and those records would be displayed first. You may handle this separately if needed.
I had a similar issue and the trick that did it for me was this one
*"ORDER BY LENGTH(column_name), column_name
As long as the non-numeric part of the value is the same length, this will sort 1 before 10, 10 before 100, etc."*
as given by Andreas Bergström on this question.
Hope that helps someone.
this is the alphabetical order,
you want numerical order,
for do this you must in the ORDER BY clause
trim the costant "aaa-" part
convert it in number
convert(SUBSTRING(val, 3), integer)
I will give you a sample sorting. Not based on your data sample, but this could help you out.
Say you have data like this :
id
----
1
2
6
10
13
when you do ORDER BY id ASC would return :
id
----
1
10
13
2
6
I suggest, use LPAD.
This query : SELECT LPAD('12',5,'0') return 00012
So when you have table data like I provide above, you can sort them like this :
SELECT * FROM TABLE
ORDER BY LPAD(ID,7,'0') ASC
Based on your data.
SELECT SUBSTR('aaa-100',5,LENGTH('aaa-100') - 3) return 100
So, SELECT LPAD( SUBSTR('aaa-100',5,LENGTH('aaa-100') - 3), 7, '0') return 00000100
So you can combine string function such as SUBSTR and LPAD. Do have any clue now?

MySQL: compare a mixed field containing letters and numbers

I have a field in the mysql database that contains data like the following:
Q16
Q32
L16
Q4
L32
L64
Q64
Q8
L1
L4
Q1
And so forth. What I'm trying to do is pull out, let's say, all the values that start with Q which is easy:
field_name LIKE 'Q%'
But then I want to filter let's say all the values that have a number higher than 32. As a result I'm supposed to get only 'Q64', however, I also get Q4, Q8 and so for as I'm comparing them as strings so only 3 and the respective digit are compared and the numbers are in general taken as single digits, not as integers.
As this makes perfect sense, I'm struggling to find a solution on how to perform this operation without pulling all the data out of the database, stripping out the Qs and parsing it all to integers.
I did play around with the CAST operator, however, it only works if the value is stored as string AND it contains only digits. The parsing fails if there's another character in there..
Extract the number from the string and cast it to a number with *1 or cast
select * from your_table
where substring(field_name, 1, 1) = 'Q'
and substring(field_name, 2) * 1 > 32

using regualr expression in mysql to select specific rows

I'm trying to select a small set of records that match a patten I have a series of numbers in each row such as
1
2
3
some of them have sub numbers
3.1
3.2
4
5
I can select only the whole numbers using
REGEXP '^[0-9]+$'
I can select all rows that have a . in them like 3.1 3.2 etc using
REGEXP '[.]{1}'
but I can't seem to select for example only sub numbers that start with 3 I've tried
REGEXP '[^3.]{1,}'
but that returns all records
Ideally I want to return only records that are in the format of 3.1 I would like to define the start number and the dot so 3. then the second part match against the records
I hope this makes sense
I used '3\.[0-9]{1,}' - it matched.
Yours probably fails because of unescaped dot - ., which matches every character.
Escape characters with \
Format 3.d where d is digit:
3\\.[0-9]

MySql ordering problem

Consider the situation i have a table name "test"
-------
content (varchar(30))
-------
1
abc
2
bcd
-------
if i use order by
Select * from test order by content asc
i could get result like
--------
content
--------
1
2
abc
bcd
---------
but is there any way i could get the following result using query
--------
content
--------
abc
bcd
1
2
---------
To get by the collation, you can do by testing the first character... it appears you want anything starting with a numeric to be after anything alhpa oriented... something like the ISNUMERIC() representation by Ted, but my quick check doesn't show such function in MySQL.. So an alternative... because numerics in ASCII list are less than "A" (char 65)
Select *
from test
order by
case when left( content, 1 ) < "A" then 2 else 1 end,
content
Although I've seen different CONVERT() calls, I don't have MySQL available to confirm. However, in addition to the above case/when, you can add a SECOND case/when and call some UDF() or other convert function on the "content" value. If the string starts as alpha, it should return a zero value so the first case/when will keep them to the top of the list, then since all are all non-convertible to numeric would have a value of zero... no impact on the sort, then finally the content itself which will keep in alpha order.
HOWEVER, if your second case/when / convert function call DOES return a numeric value, then it will be properly sorted within the numeric grouping segment... which will then supercede that of the content... However, if content was something like
100 smith rd and
100 main st
they will sort in the same "100" category numeric value, but then alphabetically by the content as
100 main st
100 smith rd
100
this will do it:
SELECT *
FROM test
ORDER BY CAST(field AS UNSIGNED), field ASC
select * from sometable order by content between '0' and '9', content
Not sure on MySql but on SQL Server you can do this...
SELECT * FROM test
ORDER BY IsNumeric(content), content
The order of results is defined by collation used, so if you can find the right collation then yes.
http://dev.mysql.com/doc/refman/5.0/en/charset-collate.html
//edit
This is tricky. I've done some research and it seems that no currently available collation can do that. However there's also possibility to add new collation to MySQL. Here's how.