'How to know CAST failed in MySQL

Could somebody tell me how I can detect if a cast failed in MySQL using CAST() function?

These two lines return the same value: 0.

SELECT CAST('Banana' AS UNSIGNED INTEGER) AS 'CAST1';
SELECT CAST('0' AS UNSIGNED INTEGER) AS 'CAST2';


Solution 1:[1]

The SHOW WARNINGS statement and the @@WARNINGS system variable are the built in methods to do this. There is no mechanism to automatically upgrade all warnings to errors, but there are some things you can do.

You may want to start MySQL with the --show-warnings option, although that might just display the count of warnings with the row count. I can't recall anymore. I don't know if there is a my.ini option for this option. There's also the --log-warnings option, which I believe does have an option in the ini/cnf file. If you're executing a script or using the CLI, the \W command turns show warnings on and \w turns them off for (IIRC) the current connection.

You may also want to look at the SQL mode. TRADITIONAL is probably the most like a normal RDBMS, but it's kind of a rats nest of options. The STRICT modes are what you're most likely after, but read through that page. Most apps built on MySQL take advantage of the (non-deterministic) GROUP BY extensions that bite just about everybody moving to or away from MySQL, and TRADITIONAL enables ONLY_FULL_GROUP_BY, which effectively disables those extensions and the RDBMS doesn't support the OVER() clause. I don't know if silently succeeding at typecasting will abort a transaction even in traditional/strict mode, however.

MySQL is kind of a mine field of these kinds of issues (e.g., zero dates) so it kind of has a poor reputation with DBAs, especially those who worked with v3.x or v4.x.

Solution 2:[2]

You can use regular expressions to validate the data before the conversion:

select (case when val regexp '^[0-9]+$' then cast(val as unsigned integer) end)

Solution 3:[3]

You could e.g. check the warning_count variable:

MySQL [test]> SELECT CAST(0 AS UNSIGNED INTEGER) AS 'CAST1', @@warning_count;
+-------+-----------------+
| CAST1 | @@warning_count |
+-------+-----------------+
|     0 |               0 |
+-------+-----------------+
1 row in set (0.01 sec)

MySQL [test]> SELECT CAST('Banana' AS UNSIGNED INTEGER) AS 'CAST1', @@warning_count;
+-------+-----------------+
| CAST1 | @@warning_count |
+-------+-----------------+
|     0 |               1 |
+-------+-----------------+
1 row in set, 1 warning (0.00 sec)

There's a caveate though: the warning count is only reset per statement, not per result row, so if CAST() gets executed mutiple times, e.g. for each result row, the counter will go up on each failed invocation.

Also warnings don't seem to get reset on successful queries that don't touch any tables, so in the example above a 2nd

SELECT CAST(0 AS UNSIGNED INTEGER) AS 'CAST1', @@warning_count;

will still show 1 warning, while e.g.

SELECT CAST(0 AS UNSIGNED INTEGER) AS 'CAST1', @@warning_count
  FROM mysql.user LIMIT 1;

will correctly reset it to 0 ...

Solution 4:[4]

Well, you could incorporate @@warning_count but somehow create workaround for its buggy functionality.

Take a look at this code below. Yes, I know it's ugly, but it works.

SELECT
        IF(WarningCount = 0, ConversionResult, NULL)
FROM (
    SELECT 
            CAST('banana' AS DECIMAL(10, 6)) AS ConversionResult
        ,   @@warning_count AS WarningCount
    FROM <any non empty table>
    LIMIT 1
) AS i;

In inner SELECT I'm getting 1 row (LIMIT 1) from any existing table. I'm converting string ('banana') and get WarningCount. In outer SELECT I'm checking WorkingCount and if it's equal to 0 (conversion successful) then returning converted value.

Solution 5:[5]

I would suggest to use such function:

drop function if exists to_number;
delimiter $$
create  function to_number (number varchar(10)) returns int
begin
    declare error_message varchar(45);
    if (number regexp ('^[+-]?[0-9]*([0-9]\\.|[0-9]|\\.[0-9])[0-9]*(e[+-]?[0-9]+)?$'))
    then
      return number;
    else
      set error_message = concat('The given value "', number, '" is not a number.');
      signal sqlstate '45000' set message_text = error_message;
    end if;
end;

It rises an error if the given value is not number or returns the number value.

Solution 6:[6]

If you are trying to determine how many values in a varchar are numbers you can try:

select count(*),
       sum(is_num)
 from (select case when cast(cast(ar_number as unsigned) as char) = ar_number then 1 else 0  end as is_num
 from the_table) as t1;

Solution 7:[7]

SQL Server supports the try_cast function

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bacon Bits
Solution 2 Gordon Linoff
Solution 3 Hartmut Holzgraefe
Solution 4 Marek J
Solution 5 PavelPraulov
Solution 6 John Darrah
Solution 7 Raul