Search code examples
castingdecimalvarcharteradataisnumeric

Safe casting VARCHAR to DECIMAL in Teradata


In Teradata DB I have source table

create set table SRC_TABLE (
    Some_Id varchar(2O) not null
);

This table is loaded with data from external system. I have target table

create set table DST_TABLE (
    Some_Id decimal(4,0) not null
);

I need to copy rows from SRC_TABLE to DST_TABLE safely. There is a contract in place that external system will provide only values convertible to DECIMAL(4). However, is there any safe way how to select rows in SRC_TABLE which are not compliant with contract and may cause typecasting failure?

Update: I cannot use UDF functions due to restrictions in environment I am working in.


Solution

  • Finally with help of my colleagues I came to feasible solution. It has some limitations (sign is not considered, decimal part is not considered), but for IDs it works just fine.

    1. Trim spaces from both beginning and end of string
    2. Trim leading zeros from string
    3. Test for maximum allowed length
    4. Pad string with zeros to four chars (add four zeros to beginning of string and get last four characters from string)
    5. Test each position in string on set of allowed chars

    So the records from SRC_TABLE which cannot be converted to DECIMAL(4) can be obtained by select:

    select 
      Some_Id
    from
      SRC_TABLE
    where
      characters(trim(leading '0' from trim(both ' ' from Some_Id))) > 4
      or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 1 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9')  
      or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 2 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9')
      or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 3 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9')
      or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 4 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9');
    

    EDIT: More convenient is the way suggested by dnoeth in his answer to Convert char to int TeraData Sql , which also works in TD 13.10:

    -- TO_NUMBER returns NULL when failing
    
    CAST(TO_NUMBER(UTENTE_CD) AS INTEGER)
    
    -- check if there are only digits
    CASE WHEN UTENTE_CD  = ''                     -- all spaces
           THEN NULL
         WHEN LTRIM(UTENTE_CD, '0123456789') = '' -- only digits
           THEN CAST(UTENTE_CD AS INTEGER)
         ELSE NULL
    END