Using ISNUMERIC to determine JOIN criteria?

  • I am maintaining a system that compares Customer address data with a master table containing address ranges. A vast simplification of my current task might be:

    A customer may live at 123 Oak Street, and I need to find the address range row from the master table that the customers address "fits" into.

    The problem arises in that the table data is Char, including the address "numbers" I wish to compare.

    Consider this :

    CREATE TABLE #address_ranges (

    Streetchar(20),

    low_addchar(10),

    high_addchar(10))

    CREATE TABLE #cust_address (

    cust_numint,

    cust_namechar(50),

    Streetchar(20),

    add_numberchar(10))

    INSERT INTO #address_ranges

    (Street, low_add, high_add)

    SELECT 'Oak', '1', '2100'

    UNION ALL

    SELECT 'Oak', '1200', '1999'

    UNION ALL

    SELECT 'Oak', '18300', '99999'

    INSERT INTO #cust_address

    (cust_num, cust_name, Street, add_number)

    SELECT 1, 'My Restaurant', 'Oak', '19207'

    UNION ALL

    SELECT 2, 'My Gas', 'Oak', '2101'

    UNION ALL

    SELECT 3, 'My coffee', 'Oak', '9239'

    If I now query the tables looking for matches, customer "My Restaurant" gets hits in the #Address_ranges table based on CHAR comparisons of the street number, giving incorrect results.

    SELECT *

    FROM #cust_address ca

    INNER JOIN #address_ranges ad

    on ca.Street = ad.Street

    AND ca.add_number BETWEEN ad.low_add AND ad.high_add

    So I simply converted the values in question to INTs and get the desired result:

    SELECT *

    FROM #cust_address ca

    INNER JOIN #address_ranges ad

    on ca.Street = ad.Street

    AND convert(int,ca.add_number) BETWEEN convert(int,ad.low_add) AND convert(int,ad.high_add)

    Along comes my root problem. The Char nature of the #cust_address add_number column has allowed legacy data into the add_number column that is not numeric. For example :

    INSERT INTO #cust_address

    (cust_num, cust_name, Street, add_number)

    SELECT 4, 'My Corner Store', 'Oak', '455 1/2'

    Now my improved comparison criteria blows up with a non-numeric in the add_number column. What I had hoped to do was to replace the JOIN (or use a WHERE clause instead) that used a CASE statement to determine if the CONVERTED values or original values should be used for the comparison, something like:

    SELECT *

    FROM #cust_address ca

    INNER JOIN #address_ranges ad

    on ca.Street = ad.Street

    WHERE

    CASE isnumeric(ca.add_number)

    WHEN 1

    THEN convert(int,ca.add_number) BETWEEN convert(int,ad.low_add) AND convert(int,ad.high_add)

    ELSE

    ca.add_number BETWEEN ad.low_add AND ad.high_add

    END

    I realize this syntax is faulty, but I use it as pseudo-code to illustrate what I am trying to accomplish. I need to incorporate a different comparison criteria based on the contents of the add_number column. I am hitting a brain block on this one, although it seems it should be a simple work-around.

    Suggestions appreciated.

  • Does this help? The code below does nothing for character comparisons - it just matches integer values...

    One thing to note with ISNUMERIC - it will return true for values like "$" and "." which is because ISNUMERIC returns 1 for the money datatype (in addition to the expected int, decimal and float datatypes)...

    In the quite impossible case that someone's add_number is "$100" ISNUMERIC will return 1 and the code will fail at the conversion to int

    USE tempdb;

    GO

    CREATE TABLE #address_ranges (

    Street char(20),

    low_add char(10),

    high_add char(10))

    CREATE TABLE #cust_address (

    cust_num int,

    cust_name char(50),

    Street char(20),

    add_number char(10))

    INSERT INTO #address_ranges

    (Street, low_add, high_add)

    SELECT 'Oak', '1', '2100'

    UNION ALL

    SELECT 'Oak', '1200', '1999'

    UNION ALL

    SELECT 'Oak', '18300', '99999'

    INSERT INTO #cust_address

    (cust_num, cust_name, Street, add_number)

    SELECT 1, 'My Restaurant', 'Oak', '19207'

    UNION ALL

    SELECT 2, 'My Gas', 'Oak', '2101'

    UNION ALL

    SELECT 3, 'My coffee', 'Oak', '9239'

    UNION ALL

    -- inserting character values in address

    SELECT 3, 'My coffee', 'Oak', 'CharVal'

    SELECT * FROM #address_ranges

    SELECT * FROM #cust_address

    --SELECT *

    --FROM #cust_address ca

    --INNER JOIN #address_ranges ad

    --ON ca.Street = ad.Street

    --AND CAST(ca.add_number as int) BETWEEN

    --CAST(ad.low_add as int) AND CAST(ad.high_add as int)

    SELECT *

    FROM #cust_address ca

    INNER JOIN #address_ranges ad

    ON ca.Street = ad.Street

    WHERE

    (CASE ISNUMERIC(ca.add_number) WHEN 1 THEN

    CASE WHEN CAST(ca.add_number as int) BETWEEN

    CAST(ad.low_add as int) AND CAST(ad.high_add as int)

    THEN 1

    ELSE 0

    END

    END)=1

    DROP TABLE #address_ranges

    DROP TABLE #cust_address

  • Assuming you're using SQL 2005 from the forum posted in, try this:

    ;with

    AddNumCTE (ID, AddNum) as

    (select cust_num, left(add_number, charindex(' ', add_number, 0))

    from #cust_address

    where isnumeric(left(add_number, charindex(' ', add_number, 0))) = 1),

    AddStrCTE (ID, AddStr) as

    (select cust_num, add_number

    from #cust_address

    where isnumeric(left(add_number, charindex(' ', add_number, 0))) = 0)

    select *

    from #cust_address

    left outer join AddNumCTE

    on cust_num = AddNumCTE.ID

    left outer join AddStrCTE

    on cust_num = AddStrCTE.ID

    inner join #address_ranges

    on AddNum >= cast(low_add as int)

    and AddNum <= cast(high_add as int)

    or AddStr >= low_add

    and AddStr <= high_add;

    Same sample data you used.

    Will that accomplish what you need?

    It won't work in 2000, but you could accomplish the same thing there with standard derived tables in the From clause.

    - Gus "GSquared", RSVP, OODA, MAP, NMVP, FAQ, SAT, SQL, DNA, RNA, UOI, IOU, AM, PM, AD, BC, BCE, USA, UN, CF, ROFL, LOL, ETC
    Property of The Thread

    "Nobody knows the age of the human race, but everyone agrees it's old enough to know better." - Anon

Viewing 3 posts - 1 through 2 (of 2 total)

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