views:

73

answers:

1

Hi, i'm playing around with building a sql function that will extract numbers from a title, which is what the following code below does. Although, i want to modify this function to parse numbers into sections. For example:

Current Data in title field:

QW 1 RT 309-23-1
QW 1 RT 29-1
QW 1 RT 750-1
QW RT 750-1

Temp tables created once function is ran on title field:

column 1  Column 2  Column 3  Column 4
1         309       23        1
1         29        1         Null
1         750       1         Null
Null      750       1         Null

create function [dbo].[ExtractNumbers](@Numbers nvarchar(2000))  
returns nvarchar(2000)  
as 
BEGIN 
  declare @NonNumericIndex int  
  set @NonNumericIndex = PATINDEX('%[^0-9]%',@Numbers)  

  WHILE @NonNumericIndex > 0  
  begin 
    SET @Numbers = REPLACE(@Numbers,SUBSTRING(@Numbers,@NonNumericIndex,1),'')  
    SET @NonNumericIndex = PATINDEX('%[^0-9]%',@Numbers)  
    SET 
  end 

  return @Numbers  
END 
A: 

Here's one way.

Although actually at the end I realised the format was more fixed than I had originally realised so you may be better off just using the various string manipulation functions to calculate the columns directly.

WITH TestTable AS
(
SELECT 'QW 1 RT 309-23-1' AS title UNION ALL
SELECT 'QW 1 RT 29-1' UNION ALL
SELECT 'QW 1 RT 750-1' UNION ALL
SELECT 'QW RT 750-1' 
)
SELECT title, [1] AS [Column 1], [2] AS [Column 2],[3] AS [Column 3],[4] AS [Column 4]
FROM TestTable CROSS APPLY dbo.GetNumbers(title)
PIVOT 
(MAX(num) FOR idx IN ([1], [2],[3],[4])
) AS PivotTable;

Uses the following TVF

CREATE FUNCTION GetNumbers 
(
@Numbers NVARCHAR(2000) 
)
RETURNS @Results TABLE 
(
idx INT IDENTITY(1,1),
num INT
) 
AS
BEGIN
    DECLARE @NonNumericIndex INT,  @NumericIndex INT

    SET @NumericIndex = PATINDEX('%[0-9]%',@Numbers)

    IF (@NumericIndex > 4) --First Column not there
          INSERT INTO @Results VALUES (NULL)

    WHILE @NumericIndex > 0
    BEGIN
        SET @Numbers = RIGHT(@Numbers,LEN(@Numbers)-@NumericIndex+1)

        SET @NonNumericIndex = PATINDEX('%[^0-9]%',@Numbers)

        IF(@NonNumericIndex = 0)
        BEGIN
            INSERT
            INTO @Results VALUES (@Numbers)
            RETURN
        END
        ELSE
        INSERT
        INTO @Results VALUES
            (LEFT(@Numbers,@NonNumericIndex-1))

        SET @Numbers = RIGHT(@Numbers,LEN(@Numbers)-@NonNumericIndex+1)
        SET @NumericIndex = PATINDEX('%[0-9]%',@Numbers)
    END
    RETURN
END
Martin Smith
wow, great input. it works good for the data that has a "1" at position 4 but titles such as 'QW RT 750-1' will put "750" into col 1 instead of a null.
gates
It shouldn't do. I tested it with that. That's what the line `IF (@NumericIndex > 4)` is for. I'll just double check this end.
Martin Smith
Nope definitely when I run the code in the post above it puts it into Column2 my end.
Martin Smith
yes you're right, when the title inputs are hard coded in the select statement it runs correctly, but can it be tweaked to run on the title field dynamically? i have tons of records. i received the error when i just tried to run: 'SELECT title, [1] AS [Column 1], [2] AS [Column 2],[3] AS [Column 3],[4] AS [Column 4] FROM TestTable CROSS APPLY dbo.GetNumbers(title) PIVOT (MAX(num) FOR idx IN ([1], [2],[3],[4]) ) AS PivotTable;' .thank you so much for your post!
gates
What error? Did you replace `TestTable` with the name of your table?
Martin Smith
yes, i ran it on my table and the data was not parse out correctly for records most of the records.
gates
nevermind, i got it working. thanks your great!
gates
@gates - Can you put it in another database that's set to a greater compatibility level and call it from your 2000 compatibility one.
Martin Smith
what's interesting is that when i run the fuction on a table that only has the "title" field it works fine but when i run it on a table that has multiple fields that are text, it gives me the following error msg: Pivot grouping columns must be comparable. the type of colulmn "a text col name" is "text", which is not comparable."
gates
Yopu might need to use `CAST(columnname as varchar(max))` to use it on text fields.
Martin Smith
the title field alone isn't a problem. it's when title lives in a table with other fields.
gates
Martin, did you try running the function against a table with more than one field?
gates
What are you trying to do? Apply the function to more than one field at a time? If so you'll need a UNION as it wouldn't be able to share the output columns 1-4. If it is just the presence of the extra fields use a derived table. So instead of `FROM TestTable` it would be `FROM (SELECT title FROM yourtable) t`
Martin Smith
awesome!!! "FROM (SELECT title FROM yourtable) t" worked.
gates
hey martin, why does the function strip out duplicates? for example, if the data has two records with the title QW RT 750-1, only when will be returned.
gates
nevermind, looks like i just have to add the record id for all records to show up...so that's working...
gates
Hey martin, is it possible to display other table columns/fields within the same query. for instance, i would wrap the fuction on [title] but want the output view to be [title] [date] [desc] as i'm using the parsed fields to sort the list.
gates
Yes. Just add them to the main Select list and the derived table select list. Both are keywords/reserved words so you might need square brackets around them `[date], [desc]`
Martin Smith
ok, figured it out, you have to place the field names in both select statements. thanks again for everything.
gates