2

I have a Data field that is supposed to have floating values(prices), however, the DB designers have messed up and now I have to perform aggregate functions on that field. Whereas 80% of the time data is in correct format,eg. '80.50', sometime it is saved as '$80.50' or '$80.50 per sqm'.

The data field is nvarchar. What I need to do is extract the floating point number from the nvarchar. I came accross this: Article on SQL Authority

This, however, solves half my problem, or compound it, some might say. That function just returns the numbers in a string. That is '$80.50 per m2'will return 80502. Obviously that wont work. I tried to change the Regex from => PATINDEX('%[^0-9]%', @strAlphaNumeric) to=> PATINDEX('%[^0-9].[^0-9]%', @strAlphaNumeric) doesnt work. Any help would be appreciated.

1
  • what you expect from "$80.50 per m2" in answer?? Commented Nov 21, 2013 at 9:32

3 Answers 3

3

This will do want you need, tested on (http://sqlfiddle.com/#!6/6ef8e/53)

DECLARE @data varchar(max) = '$70.23 per m2'
Select LEFT(SubString(@data, PatIndex('%[0-9.-]%', @data), 
                  len(@data) - PatIndex('%[0-9.-]%', @data) +1
                 ), 
        PatIndex('%[^0-9.-]%', SubString(@data, PatIndex('%[0-9.-]%', @data), 
                  len(@data) - PatIndex('%[0-9.-]%', @data) +1))
        )

But as jpw already mentioned a regular expression over a CLR would be better

Sign up to request clarification or add additional context in comments.

2 Comments

This will miss values like 80.50 and $80.50 and return blanks.
I tested it with your values, but couldnt find a value that would work except 123. 55 but a RegEx would fail this one too
2

This should work too, but it assumes that the float numbers are followed by a white space in case there's text after.

// sample data
DECLARE @tab TABLE (strAlphaNumeric NVARCHAR(30))
INSERT @tab VALUES ('80.50'),('$80.50'),('$80.50 per sqm')

// actual query
SELECT 
  strAlphaNumeric AS Original, 
  CAST (
    SUBSTRING(stralphanumeric, PATINDEX('%[0-9]%', strAlphaNumeric), 
      CASE WHEN PATINDEX('%[ ]%', strAlphaNumeric) = 0 
      THEN LEN(stralphanumeric) 
      ELSE 
      PATINDEX('%[ ]%', strAlphaNumeric) - PATINDEX('%[0-9]%', strAlphaNumeric)
      END
    ) 
    AS FLOAT) AS CastToFloat
FROM @tab

From the sample data above it generates:

Original                       CastToFloat
------------------------------ ----------------------
80.50                          80,5
$80.50                         80,5
$80.50 per sqm                 80,5

Sample SQL Fiddle.

If you want something more robust you might want to consider writing an CLR-function to do regex parsing instead like described in this MSDN article: Regular Expressions Make Pattern Matching And Data Extraction Easier

Comments

0

Inspired on @deterministicFail, I thought a way to extract only the numeric part (although it's not 100% yet):

DECLARE @NUMBERS TABLE (
    Val VARCHAR(20)
)
INSERT INTO @NUMBERS VALUES
('$70.23 per m2'),
('$81.23'),
('181.93 per m2'),
('1211.21'),
(' There are 4 tokens'),
('  No numbers    '),
(''),
('  ')
select
    CASE
        WHEN ISNUMERIC(RTRIM(LEFT(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))), LEN(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))))- PATINDEX('%[^0-9.-]%',RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val))))))))=1 THEN
            RTRIM(LEFT(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))), LEN(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))))- PATINDEX('%[^0-9.-]%',RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))))))
        ELSE '0.0'
    END
FROM @NUMBERS n

3 Comments

Produces nice output but dear god getting there is messy. 46 x LEFT/RIGHT/LTRIM/RTRIM
@OGHaza, I agree. Depending on the input string, you could remove ltrim and rtrim. I've used these functions when I inserted the blanked string ' ' as a testing case.
good point. jpw's answer falls over if there is leading whitespace - though of course he can also trim the input.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.