Reputation: 109
I got to sort strings that do not share the same format and I got trouble finding a solution for this one. Tried several options on stackoverflow for this but it does not work for me because they are for specific formatted data.
Here are an example of data i must sort.
12-ABC
1-ABC
ABC-10
ABC-11
ABC-100
2-ABCD
ABC-100A
I got results like this with ORDER BY
1-ABC
12-ABC
2-ABCD
ABC-10
ABC-100
ABC-100A
ABC-11
But i would like to have this
1-ABC
2-ABCD
12-ABC
ABC-10
ABC-11
ABC-100
ABC-100A
How would you do it?
Upvotes: 0
Views: 96
Reputation: 109
Thx guys for all your infos. I manage to get the thing works almost perfectly with this particular case.
select distinct PCE_NAM,
--FIRST SECTION OF THE STRING
--REPLICATE to fill 0s before numerics
(CASE
WHEN CHARINDEX('-', PCE_NAM) = 0 then PCE_NAM
WHEN CHARINDEX('-', PCE_NAM) > 0 and
ISNUMERIC(LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)) = 1 and
LEN(LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)) <= 8
then REPLICATE('0', 8-LEN(LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1))) + LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)
ELSE LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)
END) as FirstPart,
--SECOND SECTION OF THE STRING
(CASE
WHEN CHARINDEX('-', PCE_NAM) = 0 then ''
WHEN CHARINDEX('-', PCE_NAM) > 0 and
ISNUMERIC(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))) = 1 and
LEN(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))) <= 8
then REPLICATE('0', 8-LEN(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)))) + SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))
ELSE SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))
END) as SecondPart,
--GET THE NUMERICS ONLY FROM THE SECOND SECTION SO CAN SORT PROPERLY
(CASE
WHEN LEN(LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))))) <= 8
then REPLICATE('0', 8-LEN(
LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)))))) +
LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))))
ELSE LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))))
END) as SecondPartF
from PARTS
order by FirstPart, SecondPartF, SecondPart
Upvotes: 0
Reputation: 1269873
This is a complicated problem. Parsing strings is something that SQL is not designed for in general, and SQL Server in particular.
You are trying to extract a number from two parts of a string and sort by that number. Arrgg! That really suggests that you are storing multiple types of information in a string -- things that would perhaps be better represented in separate columns.
That said, you can do what you want. Here is one method that works on the data you supplied in the question:
select t.str
from (values ('1-ABC'),
('12-ABC'),
('2-ABCD'),
('ABC-10'),
('ABC-100'),
('ABC-100A'),
('ABC-11')
) t(str) cross apply
(values (left(str, charindex('-', str + '-') - 1), stuff(str, 1, charindex('-', str), ''))
) v(part1, part2)
order by coalesce(try_convert(int, v.part1), 999999999),
part1,
try_convert(int, left(v.part2, patindex('%[^0-9]%', v.part2 + 'x') - 1)),
part2;
Here is a db<>fiddle.
Upvotes: 3
Reputation: 1107
You could split the string into number and text (on the '-') and sort then by the num column. Something like:
select iif((charindex('-', str) > 0), (cast(substring(str, 1, (charindex('-', str)-1)) as integer)), 9999) as num, str
from (
select '1-ABC' as str union all
select '2-ABC' as str union all
select '12-ABC' as str union all
select 'ABC' as str
) tbl
order by num, str
This way you will sort by the numerical value first and then by the string. Here I just add a really high number to the none number values so the it will be ordered last.
Upvotes: 0