Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
rrb
SQLTeam Poet Laureate
1479 Posts |
Posted - 2007-03-27 : 20:45:12
|
Apologies if this has been done before, but I couldn't find a completed example. If anyone has time, I'd love to see some improvements...Where's fribble when you need him?/* function: numeric_order arguments: @numeric_string - a string of mixed alpha and numeric values @max_digits - the maximum length of digits to compare description: Function numeric_order creates an orderable string based on the "numeric" value of @numeric_string which can be ordered alphabetically. Ideally the strings should really be broken up into constituent parts and ordered properly, but occasionally you come across data where it's just not worth the while. eg select title from regulations order by title returns: Regulation 1(a) section 3 Regulation 11 section 100(b) Regulation 11 section 2(c)(iii) Regulation 2 section 1 Regulation 21 section 3 (b) whereas select title from regulations order by dbo.numeric_order(title,10) returns: Regulation 1(a) section 3 Regulation 2 section 1 Regulation 11 section 2(c)(iii) Regulation 11 section 100(b) Regulation 21 section 3 (b) which is the order most users would expect. NOTE: because the original strings are mixed, the user may include alphas between digits which are to be sorted alphabetically, which means the string must keep all alpha parts of the original string as is. expected output: select dbo.numeric_order('Part 1(b) subsection 1',4) returns Part 0001(b) subsection 0001 test data: use test --go if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[regulations]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[regulations] --go create table regulations (id int identity(1,1), title varchar(200)) --go insert into regulations (title) select 'Regulation 1(a) section 3' insert into regulations (title) select 'Regulation 11 section 100(b)' insert into regulations (title) select 'Regulation 11 section 2(c)(iii)' insert into regulations (title) select 'Regulation 2 section 1' insert into regulations (title) select 'Regulation 21 section 3 (b)' --go select title as [Incorrectly Ordered] from regulations order by title --go select title as [Correctly Ordered] from regulations order by dbo.numeric_order(title, 10) --go drop table [dbo].[regulations] --go*/if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[numeric_order]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[numeric_order]gocreate function [dbo].[numeric_order]( @numeric_string as varchar(1000), @max_digits as int) returns varchar(8000) as begin declare @return varchar(8000) declare @part varchar(1000) declare @digit_position int declare @rest varchar(1000) declare @non_digit_position int declare @numeric_term varchar(1000) declare @after varchar(1000) declare @between varchar(1000) declare @first_time int declare @digits varchar(100) --create a string of zeros equal in length to max number of digits of enclosed numeric values set @digits = replace(space(@max_digits),' ','0') --handle length issues --worst case scenario is a number every second character multiplied by padlen < 8000 --which would potentially add len(@numeric_string)/2 * padlen characters - so subtract this from the string set @part = left(@numeric_string, ((len(@numeric_string)/2) * @max_digits)) --starting values set @non_digit_position = 0 set @first_time = 1 set @return = '' --loop while not at end of string while ((@non_digit_position > 0 or @first_time > 0) and (len(@part) > 0)) begin --if there are digits in the string set @digit_position = patindex('%[0-9]%', @part) if @digit_position > 0 begin --get the part of the string after the first digit set @rest = substring(@part, patindex('%[0-9]%', @part) + 1, len(@part)-patindex('%[0-9]%', @part) + 1) set @non_digit_position = patindex('%[^0-9]%',@rest) --extract the string of digits set @numeric_term = case when @non_digit_position > 0 then substring(@part, @digit_position, @non_digit_position) else substring(@part, @digit_position, len(@part) - @digit_position + 1) end --keep track of the rest of the string after and between the digits set @after = substring(@part, @digit_position + len(@numeric_term), len(@part) - @digit_position + @non_digit_position) set @between = '' + substring(@part, 1, @digit_position - 1) --build return string set @return = @return + @between + right(@digits + @numeric_term, @max_digits) end else begin --no more digits, just add back the rest of the original string set @return = @return + @part set @after = '' end --iterate set @first_time = 0 set @part = @after end return @returnendgo --I hope that when I die someone will say of me "That guy sure owed me a lot of money" |
|
Fantasiiio
Starting Member
1 Post |
Posted - 2007-04-19 : 15:17:55
|
hmmm.. yeah.. if you need something less complicated, try thisCREATE function [dbo].[numeric_order]( @numeric_string as varchar(1000), @max_digits as int) returns varchar(8000) as begin declare @return varchar(8000) declare @PaddingChar AS NVARCHAR(1000) set @PaddingChar = '' declare @i as integer set @i = 0 while (@i < @max_digits) begin set @PaddingChar = @PaddingChar + '0' set @i = @i + 1 end set @return = SUBSTRING(@PaddingChar, 0, @max_digits - LEN(@numeric_string)) + @numeric_string return @returnend |
|
|
rrb
SQLTeam Poet Laureate
1479 Posts |
Posted - 2007-04-19 : 19:43:55
|
Hi FantasiiioNot meaning to sound pedantic, but just in case others are looking for a solution - note that your code only works for a single numeric value in the string - not for repeated numerics (as described in the comments above the code), and it also fails when numeric_string is longer than max_digits. (Note the test data included in the code comments).However, I'd love to see an improvement on the code I posted above if you have the time to work on it. I'm not a great coder and I couldn't work out how to aviod passing in the max_digits parameter without the code getting a lot longer and more complicated - that's where we need fribble! - I was going to scan through the string first to work out the longest numeric, but I wasn't sure about the hit on resources and whether this was overkill, since you won't know in advance how many numerics there could be in a string.It would be really cool not to have to pass in the max_digits parameter because then the function would work in the general case, without needing to know anything about what was contained.Anyhow, thanks for posting backthere once was a yak named fantasiiiowho coded away in her studioshe laughed and she winkedand her code was succinctand what it lacked, she made up for, with enthusio!(Sorry if you're not a "her" - couldn't tell from your profile)CheersPS, It's Grumpy-day here in Oz.--I hope that when I die someone will say of me "That guy sure owed me a lot of money" |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2007-04-20 : 02:32:17
|
I haven't had the time to compare speed.declare @regulations table (id int identity(1,1), title varchar(200))insert into @regulations (title) select 'Regulation 1(a) section 3'insert into @regulations (title) select 'Regulation 11 section 100(b)'insert into @regulations (title) select 'Regulation 11 section 2(c)(iii)'insert into @regulations (title) select 'Regulation 2 section 1'insert into @regulations (title) select 'Regulation 21 section 3 (b)'select * from @regulationsselect titlefrom ( SELECT title, dbo.fnfilterstring(title, '%[0-9]%', '#', 1) as data from @regulations ) as dorder by cast(dbo.fnparsestring(-1, '#', data) as int), cast(dbo.fnparsestring(-2, '#', data) as int) with references tohttp://www.sqlteam.com/forums/topic.asp?TOPIC_ID=76033http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=79083Peter LarssonHelsingborg, Sweden |
|
|
rrb
SQLTeam Poet Laureate
1479 Posts |
Posted - 2007-04-20 : 02:55:16
|
Hey PesoThat's looks like a much neater solution, but it doesn't quite work for me - am I using it wrong?declare @regulations table (id int identity(1,1), title varchar(200))insert into @regulations (title) select 'Regulation 15(3)'insert into @regulations (title) select 'Regulation 15(4)'insert into @regulations (title) select 'Regulation 15A(3)'insert into @regulations (title) select 'Regulation 15B'insert into @regulations (title) select 'Regulation 15C'insert into @regulations (title) select 'Regulation 15D(1)'insert into @regulations (title) select 'Regulation 15E(3)'insert into @regulations (title) select 'Regulation 15E(4)'insert into @regulations (title) select 'Regulation 15E(5)'I'm getting:Regulation 15BRegulation 15CRegulation 15D(1)Regulation 15E(3)Regulation 15(3)Regulation 15A(3)Regulation 15(4)Regulation 15E(4)Regulation 15E(5)Rather than:Regulation 15(3)Regulation 15(4)Regulation 15A(3)Regulation 15BRegulation 15CRegulation 15D(1)Regulation 15E(3)Regulation 15E(4)Regulation 15E(5)there once was a yak in australiawho tried very hard to regale ya* with ordering codefrom his southern abodebut it turned out that he was a failure*(ya is aussie for "you" as in "ya ijut")PS - I did some speed comparisons of my table of only 690 records.I tried both solutions together and then swapped the order and tried again (just in case there were caching issues.) I'm no expert at benchmarking and what precise caching sql does...but I basically just put a getdate() before and after each call.Here's my results:test 1:your solution (run first) - 563 msmy solution (run second) - 47 mstest2:my solution (run first) - 47 msyour solution (run second)- 563msso then (because I was waiting for my .NET project to compile) I tried it again with 690000 records and got these:test 3:my solution (run first) - 223.747 syour solution (run second)- 691.910 stest 4:your solution (run first) - 588.440 smy solution (run second) - 114.733 sI guess my .NET solution finished compiling during the second test...anyhow - FYICheers--I hope that when I die someone will say of me "That guy sure owed me a lot of money" |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2007-04-20 : 06:21:56
|
Add TITLE as third sort argument, as I only sorts by the numeric values.Peter LarssonHelsingborg, Sweden |
|
|
rrb
SQLTeam Poet Laureate
1479 Posts |
Posted - 2007-04-20 : 20:02:54
|
CoolThanks--I hope that when I die someone will say of me "That guy sure owed me a lot of money" |
|
|
jezemine
Master Smack Fu Yak Hacker
2886 Posts |
Posted - 2007-04-20 : 20:18:35
|
quote: Originally posted by rrbthere once was a yak in australiawho tried very hard to regale ya* with ordering codefrom his southern abodebut it turned out that he was a failure
now I see why they call ya the poet laureate www.elsasoft.org |
|
|
rrb
SQLTeam Poet Laureate
1479 Posts |
Posted - 2007-04-20 : 20:25:27
|
My highpoint - [url]http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=21054&SearchTerms=sql,night,elf[/url]The curse of the SQL Night Elf.There was-a-yak from Epsom, in SurreyWho appeared to be in somewhat of a hurryHe was bored with his tag"Starting Member? 's such a drag!""I'll give these SQLers some curry!"Now he noticed that titles were assignedbased on how many answers one could findso he posted, alrightposted all day and all nightTill his post count read nine-ninety-nineBut his luck ran out the very next dayFor while he'd been posting awayHis boss (in frustration)Had seen no data migrationAnd sacked him without any paySo forever he will no doubt remainThe yak with an automated nameThe tale of his curseNow recited in verseWarns us all to avoid seeking fame.Oh yeah - and it was all true (well except for the bits about him being stuck on 999 and being sacked by his boss - and probably most of the rest...) - ah, the sql night elf - now that is a story!--I hope that when I die someone will say of me "That guy sure owed me a lot of money" |
|
|
jezemine
Master Smack Fu Yak Hacker
2886 Posts |
Posted - 2007-04-22 : 03:15:11
|
all that and the poor guy didn't get his custom title... ...but you did! www.elsasoft.org |
|
|
|
|
|
|
|