Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 256
  • Last Modified:

Sorting subset strings in a char field in MS SQL 2008 R2

Hi EEE:

A char field contains values like:

 AB  23  43  543
AB 5 111 22
 AB 5 2 3333
XY 234 1  24
  G 112 3434 222 33
45 33 44

In other words, characters (optional) followed by numbers, all separated by variable space(s).
I would like to sort this field by characters first and then the individual number blocks. White spaces of any length determine blocks.


So for example,

45 33 44 would sort before AB 5 2 3333 as it contains no leading set of characters.
AB 5 111 22 would sort before  AB 23  43  543 as 5 comes before 23.
 AB 5 2 3333 would sort before AB 5 111 22 as 2 comes before 111 (all prior characters are
the same)

Is there a way I can do this in Microsoft SQL 2008 R2?
0
LuckyLucks
Asked:
LuckyLucks
1 Solution
 
QlemoBatchelor, Developer and EE Topic AdvisorCommented:
I daresay that not. The dynamic nature of the sort criteria does not allow for it.
0
 
Scott PletcherSenior DBACommented:
You could pass the string into a function and return a sortable value, but the performance will not be great.  How many rows like this do you need to process?
0
 
LuckyLucksAuthor Commented:
50K and likely not to exceed the 100K.
0
What Kind of Coding Program is Right for You?

There are many ways to learn to code these days. From coding bootcamps like Flatiron School to online courses to totally free beginner resources. The best way to learn to code depends on many factors, but the most important one is you. See what course is best for you.

 
Scott PletcherSenior DBACommented:
CREATE TABLE #data (
    string varchar(1000) NOT NULL
    )
INSERT INTO #data
SELECT 'AB  23  43  543' UNION ALL
SELECT ' AB 5 111 22' UNION ALL
SELECT '  AB 5 2 3333' UNION ALL
SELECT ' XY 234 1  24' UNION ALL
SELECT '   G 112 3434 222 33' UNION ALL
SELECT ' 45 33 44'

GO

CREATE FUNCTION dbo.Set_Sort_String (
    @string varchar(1000)
)
RETURNS varchar(1000)
AS
BEGIN
SET NOCOUNT ON;
SELECT
    CASE WHEN [1] LIKE '%[^0-9]%'
         THEN [1] + SPACE(10 - LEN([1]))
         ELSE SPACE(10 - LEN([1]))  + [1]
         END +
    SPACE(10 - ISNULL(LEN([2]), 0))  + ISNULL([2], '') +
    SPACE(10 - ISNULL(LEN([3]), 0))  + ISNULL([3], '') +
    SPACE(10 - ISNULL(LEN([4]), 0))  + ISNULL([4], '') +
    SPACE(10 - ISNULL(LEN([5]), 0))  + ISNULL([5], '') +
    SPACE(10 - ISNULL(LEN([6]), 0))  + ISNULL([6], '') +
    SPACE(10 - ISNULL(LEN([7]), 0))  + ISNULL([6], '') +
    SPACE(10 - ISNULL(LEN([8]), 0))  + ISNULL([7], '') +
    SPACE(10 - ISNULL(LEN([9]), 0))  + ISNULL([8], '') +
    SPACE(10 - ISNULL(LEN([10]), 0)) + ISNULL([9], '') +
    SPACE(10 - ISNULL(LEN([2]), 0)) + ISNULL([10], '')    
FROM (
    SELECT
        --replace 2+ spaces in a row with a single space
        REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(@string)),
            SPACE(33), SPACE(1)), SPACE(17), SPACE(1)), SPACE(9), SPACE(1)), SPACE(5), SPACE(1)),
            SPACE(3), SPACE(1)), SPACE(2), SPACE(1)), SPACE(2), SPACE(1)) AS string
) AS test_data
CROSS APPLY dbo.DelimitedSplit8K ( string, SPACE(1) ) ds
PIVOT (
    MAX(item)
    FOR ItemNumber IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10])
) AS pvt
END --IF

GO



SELECT *, dbo.Set_Sort_String ( string )
FROM #data
ORDER BY dbo.Set_Sort_String ( string )
0
 
LuckyLucksAuthor Commented:
Hi Scott,

Could you please add some comments so I can understand what the code is trying to do in each block?


Thanks much
0
 
Scott PletcherSenior DBACommented:
First, it's splitting it into separate values with no extra spaces, so:
 G  112    3434  222    33
would become:
G
112
3434
222
33
Then it concatenates all the values into one string, as follows:
If the first value has any nonnumeric char in it -- such as "G", like this one does -- the first 10 bytes of that value is left-aligned in the new string:
Gbbbbbbbbb
where b = blank/space.
Then every entry after that is right-aligned into the next 10 bytes:
Gbbbbbbbbbbbbbbbb112bbbbbb3434bbbbbbb222bbbbbbbb33
That is done for each row.  Then the concatenated value above is used to sort the rows.

I believe this will yield the final sort order that you want.
0
 
awking00Commented:
What is the required sort order criteria for the alpha portion? Is it like A,B,C,...Z,AA, AB, etc.which would make the string beginning with a single G sort before strings beginning with AB or should the string beginning with a single G sort after strings beginning with AB because G comes after A?
0
 
LuckyLucksAuthor Commented:
Thanks Scott, a couple of last questions:

1. What does the below try to accomplish?

SPACE(10 - ISNULL(LEN([2]), 0))  + ISNULL([2], '') +
     SPACE(10 - ISNULL(LEN([3]), 0))  + ISNULL([3], '') +
     SPACE(10 - ISNULL(LEN([4]), 0))  + ISNULL([4], '') +
     SPACE(10 - ISNULL(LEN([5]), 0))  + ISNULL([5], '') +
     SPACE(10 - ISNULL(LEN([6]), 0))  + ISNULL([6], '') +
     SPACE(10 - ISNULL(LEN([7]), 0))  + ISNULL([6], '') +
     SPACE(10 - ISNULL(LEN([8]), 0))  + ISNULL([7], '') +
     SPACE(10 - ISNULL(LEN([9]), 0))  + ISNULL([8], '') +
     SPACE(10 - ISNULL(LEN([10]), 0)) + ISNULL([9], '') +
     SPACE(10 - ISNULL(LEN([2]), 0)) + ISNULL([10], '')    

2. Where is the DelimitedSplit8K function defined?

Thanks
0
 
Scott PletcherSenior DBACommented:
Exactly what I stated above.  It puts each column into a fixed 10 byte string from left to right:
1st column = bytes 01-10
2nd column = bytes 11-20
3rd column = bytes 21-30
etc.

A fairly well-known, highly efficient splitter function.  Sorry, thought I had posted its code too:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
--NOTE: (max) data type will at least double the time to do the split!
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
     -- enough to cover VARCHAR(8000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;
GO
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

A proven path to a career in data science

At Springboard, we know how to get you a job in data science. With Springboard’s Data Science Career Track, you’ll master data science  with a curriculum built by industry experts. You’ll work on real projects, and get 1-on-1 mentorship from a data scientist.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now