Better way to do this

  • A report developer came to me with the following method of parsing out a string...the data comes from a table that is LEFT joined upon and looks like the example string below (the number of spaces between each data element can be different i.e. (2 spaces, 3 spaces, 1 space, etc)):DECLARE @string AS varchar(40)

    SET @string = ' OKALOOSA AIRPORT ABC VALPRAISO FL '

    /*State*/

    SELECT

    REVERSE(LEFT(REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*'),

    CHARINDEX('*', REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*')) - 1))

    /*City*/

    SELECT

    REPLACE((REVERSE(LEFT(REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*'),

    CHARINDEX('*', REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*'),

    CHARINDEX('*', REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*'))

    + 1) - 1))),

    '*' + REVERSE(LEFT(REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*'),

    CHARINDEX('*', REPLACE(REPLACE(REPLACE(REVERSE(LTRIM(RTRIM(@string))), ' ', '<>'), '><', ''), '<>', '*')) - 1)),

    '')

    Is there a better way? My first thought was to create a function to replace the spaces with a single space, then parse out the last 2 fields - but surely there's got to be a better less-CPU intensive way of doing this - suggestions?

    The desired output is:

    VALPRAISO, FL

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

  • Yes you can replace multiple spaces with a single space much easier than this.

    http://www.sqlservercentral.com/articles/T-SQL/68378/[/url]

    It looks like you have a mountain of nested replaces in here for a number of other scenarios. It is hard to distinguish which of those are required and which are used in the replacing of multiple spaces.

    If you take this one step further and assume you are going to get a variable number of spaces you could then use the DelimitedSplit8K function like this.

    DECLARE @string AS varchar(40)

    SET @string = ' OKALOOSA AIRPORT ABC VALPRAISO FL '

    SELECT @string = LTRIM(

    REPLACE(

    REPLACE(

    REPLACE(

    LTRIM(RTRIM(@string))

    ,' ',' '+CHAR(7)) --Changes 2 spaces to the OX model

    ,CHAR(7)+' ','') --Changes the XO model to nothing

    ,CHAR(7),'')) --AS CleanString --Changes the remaining X's to nothing

    WHERE CHARINDEX(' ',@string) > 0;

    with SortedValues as

    (

    select top 2 *, ROW_NUMBER() over(order by ItemNumber desc) as RowNum

    from dbo.DelimitedSplit8K(@string, ' ')

    order by ItemNumber desc

    )

    select MAX(case when RowNum = 2 then Item end) + ', ' + MAX(case when RowNum = 1 then Item end) as MyResult

    from SortedValues

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • This would work, however wedging it into the current query looks like an dreadful task. For the basis of my example I used @String to parse, but in relation to the actual query, @String is a column from a massive query with about 20 table joins...

    Consider something like (as an example only):

    SELECT col1 - col50, FieldNeededToParse,

    LoadsOfAggregatedColums

    FROM Table1 t1,

    INNER JOIN Table2 t2 ON t1.col = t2.col

    ...

    ...

    ...

    INNER JOIN Table20 t20 ON t15.col = t20.col

    LEFT JOIN TableWithHorribleFieldToParse crap ON crap.col = t1.col

    WHERE

    MassiveListOfColumns = Convoluted logic

    GROUP BY NonAggregatedColumns

    I guess I can try to wrap my head around it, but there has to be another way?

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

  • Sean Lange (5/19/2014)


    Yes you can replace multiple spaces with a single space much easier than this.

    http://www.sqlservercentral.com/articles/T-SQL/68378/[/url]

    It looks like you have a mountain of nested replaces in here for a number of other scenarios. It is hard to distinguish which of those are required and which are used in the replacing of multiple spaces.

    If you take this one step further and assume you are going to get a variable number of spaces you could then use the DelimitedSplit8K function like this.

    DECLARE @string AS varchar(40)

    SET @string = ' OKALOOSA AIRPORT ABC VALPRAISO FL '

    SELECT @string = LTRIM(

    REPLACE(

    REPLACE(

    REPLACE(

    LTRIM(RTRIM(@string))

    ,' ',' '+CHAR(7)) --Changes 2 spaces to the OX model

    ,CHAR(7)+' ','') --Changes the XO model to nothing

    ,CHAR(7),'')) --AS CleanString --Changes the remaining X's to nothing

    WHERE CHARINDEX(' ',@string) > 0;

    with SortedValues as

    (

    select top 2 *, ROW_NUMBER() over(order by ItemNumber desc) as RowNum

    from dbo.DelimitedSplit8K(@string, ' ')

    order by ItemNumber desc

    )

    select MAX(case when RowNum = 2 then Item end) + ', ' + MAX(case when RowNum = 1 then Item end) as MyResult

    from SortedValues

    Going off what Sean did, and not sure if this is the most performant way, I used a single CTE at the top to try to get it into a format where you could easily plug it into your existing query.

    You can select through "Select * from cte_deCrapify" to see how it's presenting the data, and then see what I attempted to do with your query. Maybe you can play around with this formatting and see if it will work better.

    Good luck!

    DECLARE @CrapTable TABLE (col int NOT NULL, CrapValue varchar(600) NOT NULL)

    INSERT INTO @CrapTable

    SELECT 24, ' OKALOOSA AIRPORT ABC VALPRAISO FL '

    UNION ALL

    SELECT 38, ' TUSCALOOSA AIRFIELD XYZ SOMEWHERE AL'

    UNION ALL

    SELECT 1978, ' PLACE HOLDER NULL CHICAGO IL '

    UNION ALL

    SELECT 294959, ' HOBBY AIRPORT PDQ HOUSTON TX '

    ;WITH cte_DeCrapify

    AS

    (

    SELECT C.col, C.TrimmedCrapValue, CD.Item, CD.RowNum

    FROM

    (

    SELECT col

    , TrimmedCrapValue = LTRIM(

    REPLACE(

    REPLACE(

    REPLACE(

    LTRIM(RTRIM(CrapValue))

    ,' ',' '+CHAR(7)) --Changes 2 spaces to the OX model

    ,CHAR(7)+' ','') --Changes the XO model to nothing

    ,CHAR(7),'')) --Changes the remaining X's to nothing

    FROM @CrapTable

    WHERE CHARINDEX(' ',CrapValue) > 0

    )

    as C

    CROSS APPLY

    (

    SELECT TOP 2 C.col

    , X.Item

    , ROW_NUMBER() OVER (

    ORDER BY X.ItemNumber DESC

    ) AS RowNum

    FROM dbo.DelimitedSplit8K(C.TrimmedCrapValue, ' ') AS X

    ORDER BY X.ItemNumber DESC

    ) AS CD

    )

    Select * from cte_DeCrapify

    -- ATTEMPT TO JOIN INTO YOUR QUERY IN AN EASIER WAY

    SELECT col1

    -- more columns

    , col50

    , CleanTable.MyResult

    , LoadsOfAggregatedColums

    FROM Table1 t1

    INNER JOIN Table2 AS t2 ON t1.col = t2.col

    -- more joins

    INNER JOIN Table20 AS t20 ON t15.col = t20.col

    LEFT JOIN (SELECT col, MAX(CASE

    WHEN RowNum = 2

    THEN Item

    END) + ', ' + MAX(CASE

    WHEN RowNum = 1

    THEN Item

    END) AS MyResult FROM cte_deCrapify

    ) as CleanTable ON CleanTable.col = t1.col

    WHERE MassiveListOfColumns = Convolutedlogic

    GROUP BY NonAggregatedColumns

  • A slight modification to Sean's code to avoid the REPLACEs.

    DECLARE @string AS varchar(40)

    SET @string = ' OKALOOSA AIRPORT ABC VALPRAISO FL ';

    with SortedValues as

    (

    select top 2 *, ROW_NUMBER() over(order by ItemNumber desc) as RowNum

    from dbo.DelimitedSplit8K(@string, ' ')

    WHERE Item > ''

    order by ItemNumber desc

    )

    select MAX(case when RowNum = 2 then Item end) + ', ' + MAX(case when RowNum = 1 then Item end) as MyResult

    from SortedValues

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Thanks for the suggestions guys! I'll take a deeper look over the next day or two and see what I can come up with!

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

Viewing 6 posts - 1 through 5 (of 5 total)

You must be logged in to reply to this topic. Login to reply