I have a SQL table that stores a custom item number. Each of these can have a child broken off from it with a separator of .
. Each of those can have a child too.
An example of what it could be (again, dynamic, don't know what it will be):
Item Number
1
1.1
1.1.1
1.1.1.1
1.1.1.1.a
1.1.1.1.b
10
11
2.1
2.10
2.2
2.20
20
3
30
The thing that makes this tough is those numbers are created on the fly and not necessarily in order. You may create 5 numbers (1, 2, 3, 4, 5) and then create a child of 1 so it will not be stored in order in the db.
How do I select from the table and order by the Item Number
so that it shows properly, as above, when the data isn't stored in that order?
Most solutions I've tried either gives me 1, 2, 3, 4, 5...1.1, 1.2 OR 1, 1.1, 1.1.1, 10, 11...2, 2.1, 20....3, 30, etc
.
If you have SQL 2008 you can use the new hierarchyid data type:
WITH Items (ItemNumber) AS (
SELECT '1' UNION ALL SELECT '1.1' UNION ALL SELECT '1.1.1'
UNION ALL SELECT '10' UNION ALL SELECT '11' UNION ALL SELECT '2'
UNION ALL SELECT '2.1' UNION ALL SELECT '20' UNION ALL SELECT '3'
UNION ALL SELECT '30'
)
SELECT *
FROM Items
ORDER BY Convert(hierarchyid, '/' + ItemNumber + '/');
I discussed this in another forum where we came up with an XML solution that was very dynamic. Adam Haines helped to optimize it, which dramatically improved the performance. This version includes a fix to correctly sort alphabetic digits.
Given the following values:
declare @temp table (id varchar(255))
insert into @temp (id) values
('1.1.a.1'),('1.1.aa.2'),
('1.1.b.3'),('1.1.a.4'),
('1.1.a.5'),('1.1.a.6'),
('1.1.a.7'),('1.1.a.8'),
('1.1.a.9'),('1.1.a.10'),
('1.1.a.11'),('1.1.b.1'),
('1.1.b.2'),('1.2.a.1'),
('1.10.a.1'),('1.11.a.1'),
('1.20.a.1'),('101.20.a.2'),
('1.20.a.150'),('1.1'),
('1.2'),('1')
This query:
declare @xml xml,
@max_len int
set @xml =
(
select id as id, cast('<i>' + replace(id,'.','</i><i>') + '</i>' as xml)
from @temp
for xml path('id_root'),type
)
select @max_len = max(len(x.i.value('.','varchar(10)')))
from @xml.nodes('/id_root/i') x(i)
select [id]--, srt.srtvalue
from @temp
cross apply(
select case when ISNUMERIC(x.i.value('.','varchar(10)')) = 1 then right(replicate('0',@max_len) + x.i.value('.','varchar(10)'),@max_len) else x.i.value('.','varchar(10)') end + '.'
from @xml.nodes('/id_root/i') x(i)
where x.i.value('../id[1]','varchar(50)') = [@temp].id
for xml path('')
) as srt(srtvalue)
order by srt.srtvalue
Returns these values:
id
1
1.1
1.1.a.1
1.1.a.4
1.1.a.5
1.1.a.6
1.1.a.7
1.1.a.8
1.1.a.9
1.1.a.10
1.1.a.11
1.1.aa.2
1.1.b.1
1.1.b.2
1.1.b.3
1.2
1.2.a.1
1.10.a.1
1.11.a.1
1.20.a.1
1.20.a.150
101.20.a.2
If you have more than 10 characters in a single digit, you'll have to change the varchar(10) appropriately.
-- James
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With