SQL IN query behavior - mysql

Executing the following SQL statement;
select '2312' in ('2312,254,2111') as result1, 2312 in ('2312,254,2111') as result2
I am getting the following result
+---------+---------+
| result1 | result2 |
+---------+---------+
| 0 | 1 |
+---------+---------+
I would expect the opposite result. Having result1 to be true and result2 to be false. Could someone explain why?

Using IN() with a CSV-string as parameter is not supported.
It should be IN ('2312','254','2111') instead of IN ('2312,254,2111')
The reason for the observed behaviour is an implicit type conversion happening. Look:
SELECT 2312 IN ('2312,254,2111') -- result: 1
SELECT 2312 IN ('254,2312,2111') -- result: 0 -- interesting
SELECT 2312 = '2312,254,2111' -- result: 1 << see ??
SELECT 2312 = '254,2312,2111' -- result: 0
Only the first number in the string is relevant. The rest is ignored due to the implicit type conversion.
Also,
SELECT '2312' in ('2312,254,2111') -- result: 0
is false, because no type conversion happens here. '2312' does not equal the only string '2312,254,2111' in the value list and hence the IN() operator return false.
If you use a list of values for IN() instead of a CSV-string, everything works als expected:
SELECT
2312 IN ('2312','254','2111') -- result: 1
, '2312' IN ('2312','254','2111') -- result: 1
, 254 IN ('2312','254','2111') -- result: 1
, '254' IN ('2312','254','2111') -- result: 1
, 2312 IN (2312,254,2111) -- result: 1
, '2312' IN (2312,254,2111) -- result: 1
, 254 IN (2312,254,2111) -- result: 1
, '254' IN (2312,254,2111) -- result: 1
From the manual:
Implicit type conversion may produce nonintuitive results:
mysql> SELECT 'a' IN (0), 0 IN ('b');
-> 1, 1
In both cases, the comparison values are converted to floating-point values, yielding 0.0 in each case, and a comparison result of 1 (true).

Related

MySQL type conversion while ignoring nonnumeric characters

How do I convert from varchar(500) to float in MySQL while ignoring non-numeric characters? My code currently looks like this
select distinct(customer_id), cast(val_amt as float) from(
select prsn_real_gid, vital_nam, vital_performed_date,
case when val_amt ~'^[0-9]+' then val_amt else null end as val_amt from my_table);
but I get the following error message
[Amazon](500310) Invalid operation: Invalid digit, Value 'X', Pos 2, Type: Double
Details:
-----------------------------------------------
error: Invalid digit, Value 'X', Pos 2, Type: Double
code: 1207
context: 1.XYXY04
query: 4147
location: :0
process: query0_118_4147 [pid=0]
-----------------------------------------------;
1 statement failed.
My data for example looks like this:
customer_id | val_amt
111 | 23.45
112 | 21
113 | x
114 | /
115 |
116 | 23/24
As you can see I have decimals, integers, letters, symbols and nulls. I want to ignore everything that's not a decimal or integer.
You can use regexp:
select customer_id, val_amt + 0
from my_table
where val_amt regexp '^[0-9]+[.]?[0-9]*';

How to convert from varchar to float and ignore non-numeric characters?

I am trying to convert a column from varchar(500) to float in my sql.
My code currently looks like this:
select distinct(customer_id), cast(val_amt as float) from(
select prsn_real_gid, vital_nam, vital_performed_date,
case when val_amt ~'^[0-9]+' then val_amt else null end as val_amt from my_table);
but I get the following error message
[Amazon](500310) Invalid operation: Invalid digit, Value 'X', Pos 2, Type: Double
Details:
-----------------------------------------------
error: Invalid digit, Value 'X', Pos 2, Type: Double
code: 1207
context: 1.XYXY04
query: 4147
location: :0
process: query0_118_4147 [pid=0]
-----------------------------------------------;
1 statement failed.
My data for example looks like this:
customer_id | val_amt
111 | 23.45
112 | 21
113 | x
114 | /
115 |
It has alpha characters, symbols, and nulls. I just want the numbers including the decimals.
In SQL Server you would use try_cast():
select distinct customer_id, try_cast(val_amt as float)
from my_table;
where try_cast(val_amt as float) is not null;
try this using isnumeric
select customer_id, case when isnumeric([val_amt])=0
enter code herethen 0 else cast([val_amt] as float) end from my_table
Sometimes the Cast gives me problems. Even Try_Cast(). This may be good for what you need. EDIT: You changed from SQLServer to MySQL. Adjusted for MySQL
select distinct customer_id, convert(`val_amt`, float(2,2))
from my_table
where convert(`val_amt`,float(2,2)) is not null;

Query to sum some values of a column with a condition

Having a table with this values:
name | executing | failed |
-------------------------------
task1 0 1
task2 1 0
task3 1 0
task4 0 0
With a query i want to get:
The total amount of executing task (2 in the example, task2 and task3)
The total amount of failed task (1 in the example, task1)
The total amount of pending task (those that are executing=0 and failed=0, 1 in the example, task4)
I can get the first two by uysing the following query:
SELECT IFNULL(SUM(executing), 0) Executing, IFNULL(SUM(failed), 0) Failed FROM mytable;
How can I expand my query so I can get another column with the sum of pending tasks?
Thanks in advance
Expected output:
executing | failed | pending
----------------------------
2 1 1
You don't specify how you want the results. I would do this as:
select (case when executing = 1 and failed = 0 then 'Executing'
when failed = 1 then 'Failed'
when executing = 0 and failed = 0 then 'Pending'
else 'Unknown'
end) as status, count(*) as cnt
from t
group by status;
You can also easily pivot the data using conditional aggregation:
select sum(executing = 1 and failed = 0) as Executing,
sum(failed = 1) as Failed,
sum(executing = 0 and failed = 0) as Pending
from t;
This uses a MySQL shorthand that treats boolean expressions as numbers -- with "1" for true and "0" for false.
SELECT SUM(executing) AS Executing
, SUM(failed) as Failed
, SUM(CASE
WHEN executing = 0 AND failed = 0 THEN 1
ELSE 0 END
) AS Pending
FROM mytable;

SQL convert varchar 2/3 to 0.6 ( fraction to decimal)

I have string saved in column
23.42/3 .............
I have to extract 2/3 and convert it as 0.6
Extracting is not a problem converting varchar value 2/3 to 0.6 is a issue.
2/3 is not fixed number,it can be any fraction I have to convert it to decimal
For MySQL, assuming that there is only one slash character in the string value, you can use an expression like the one aliased as res in this query:
SELECT t.str
, RIGHT(SUBSTRING_INDEX(t.str,'/',1),1)+0 AS num
, LEFT(SUBSTRING_INDEX(t.str,'/',-1),1)+0 AS den
, (RIGHT(SUBSTRING_INDEX(t.str,'/',1),1)+0)
/ (LEFT(SUBSTRING_INDEX(t.str,'/',-1),1)+0) AS res
FROM ( SELECT '1234.62/4' AS str
UNION ALL SELECT '1/4'
UNION ALL SELECT '11/16'
UNION ALL SELECT '/5'
UNION ALL SELECT 'foo/5'
UNION ALL SELECT 'fee3/4fi5/6'
UNION ALL SELECT 'bar/'
UNION ALL SELECT '/'
UNION ALL SELECT ''
) t
returns:
str num den res
----------- ------ ------ --------
1234.62/4 2 4 0.5
1/4 1 4 0.25
11/16 1 1 1
/5 0 5 0
foo/5 0 5 0
fee3/4fi5/6 3 6 0.5
bar/ 0 0 (NULL)
/ 0 0 (NULL)
0 0 (NULL)
The expression for res is just the expression for num (numerator) divided by the expression for den (denominator). Those extra columns are returned as a demonstration of how those work. Doing the division is simple.
If you need to verify that there is at most one slash, before you evaluate the expression to calculate the decimal value from the fraction, you could use an expression like this...
SELECT IF(CHAR_LENGTH(t.str)-CHAR_LENGTH(REPLACE(t.str,'/',''))=1, expr, 0)
just replace expr in that with the expression you use for res in the query above.

How do I check to see if a value is an integer in MySQL?

I see that within MySQL there are Cast() and Convert() functions to create integers from values, but is there any way to check to see if a value is an integer? Something like is_int() in PHP is what I am looking for.
I'll assume you want to check a string value. One nice way is the REGEXP operator, matching the string to a regular expression. Simply do
select field from table where field REGEXP '^-?[0-9]+$';
this is reasonably fast. If your field is numeric, just test for
ceil(field) = field
instead.
Match it against a regular expression.
c.f. http://forums.mysql.com/read.php?60,1907,38488#msg-38488 as quoted below:
Re: IsNumeric() clause in MySQL??
Posted by: kevinclark ()
Date: August 08, 2005 01:01PM
I agree. Here is a function I created for MySQL 5:
CREATE FUNCTION IsNumeric (sIn varchar(1024)) RETURNS tinyint
RETURN sIn REGEXP '^(-|\\+){0,1}([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$';
This allows for an optional plus/minus sign at the beginning, one optional decimal point, and the rest numeric digits.
Suppose we have column with alphanumeric field having entries like
a41q
1458
xwe8
1475
asde
9582
.
.
.
.
.
qe84
and you want highest numeric value from this db column (in this case it is 9582) then this query will help you
SELECT Max(column_name) from table_name where column_name REGEXP '^[0-9]+$'
Here is the simple solution for it
assuming the data type is varchar
select * from calender where year > 0
It will return true if the year is numeric else false
This also works:
CAST( coulmn_value AS UNSIGNED ) // will return 0 if not numeric string.
for example
SELECT CAST('a123' AS UNSIGNED) // returns 0
SELECT CAST('123' AS UNSIGNED) // returns 123 i.e. > 0
To check if a value is Int in Mysql, we can use the following query.
This query will give the rows with Int values
SELECT col1 FROM table WHERE concat('',col * 1) = col;
The best i could think of a variable is a int Is a combination with MySQL's functions CAST() and LENGTH().
This method will work on strings, integers, doubles/floats datatypes.
SELECT (LENGTH(CAST(<data> AS UNSIGNED))) = (LENGTH(<data>)) AS is_int
see demo http://sqlfiddle.com/#!9/ff40cd/44
it will fail if the column has a single character value. if column has
a value 'A' then Cast('A' as UNSIGNED) will evaluate to 0 and
LENGTH(0) will be 1. so LENGTH(Cast('A' as UNSIGNED))=LENGTH(0) will
evaluate to 1=1 => 1
True Waqas Malik totally fogotten to test that case. the patch is.
SELECT <data>, (LENGTH(CAST(<data> AS UNSIGNED))) = CASE WHEN CAST(<data> AS UNSIGNED) = 0 THEN CAST(<data> AS UNSIGNED) ELSE (LENGTH(<data>)) END AS is_int;
Results
**Query #1**
SELECT 1, (LENGTH(CAST(1 AS UNSIGNED))) = CASE WHEN CAST(1 AS UNSIGNED) = 0 THEN CAST(1 AS UNSIGNED) ELSE (LENGTH(1)) END AS is_int;
| 1 | is_int |
| --- | ------ |
| 1 | 1 |
---
**Query #2**
SELECT 1.1, (LENGTH(CAST(1 AS UNSIGNED))) = CASE WHEN CAST(1.1 AS UNSIGNED) = 0 THEN CAST(1.1 AS UNSIGNED) ELSE (LENGTH(1.1)) END AS is_int;
| 1.1 | is_int |
| --- | ------ |
| 1.1 | 0 |
---
**Query #3**
SELECT "1", (LENGTH(CAST("1" AS UNSIGNED))) = CASE WHEN CAST("1" AS UNSIGNED) = 0 THEN CAST("1" AS UNSIGNED) ELSE (LENGTH("1")) END AS is_int;
| 1 | is_int |
| --- | ------ |
| 1 | 1 |
---
**Query #4**
SELECT "1.1", (LENGTH(CAST("1.1" AS UNSIGNED))) = CASE WHEN CAST("1.1" AS UNSIGNED) = 0 THEN CAST("1.1" AS UNSIGNED) ELSE (LENGTH("1.1")) END AS is_int;
| 1.1 | is_int |
| --- | ------ |
| 1.1 | 0 |
---
**Query #5**
SELECT "1a", (LENGTH(CAST("1.1" AS UNSIGNED))) = CASE WHEN CAST("1a" AS UNSIGNED) = 0 THEN CAST("1a" AS UNSIGNED) ELSE (LENGTH("1a")) END AS is_int;
| 1a | is_int |
| --- | ------ |
| 1a | 0 |
---
**Query #6**
SELECT "1.1a", (LENGTH(CAST("1.1a" AS UNSIGNED))) = CASE WHEN CAST("1.1a" AS UNSIGNED) = 0 THEN CAST("1.1a" AS UNSIGNED) ELSE (LENGTH("1.1a")) END AS is_int;
| 1.1a | is_int |
| ---- | ------ |
| 1.1a | 0 |
---
**Query #7**
SELECT "a1", (LENGTH(CAST("1.1a" AS UNSIGNED))) = CASE WHEN CAST("a1" AS UNSIGNED) = 0 THEN CAST("a1" AS UNSIGNED) ELSE (LENGTH("a1")) END AS is_int;
| a1 | is_int |
| --- | ------ |
| a1 | 0 |
---
**Query #8**
SELECT "a1.1", (LENGTH(CAST("a1.1" AS UNSIGNED))) = CASE WHEN CAST("a1.1" AS UNSIGNED) = 0 THEN CAST("a1.1" AS UNSIGNED) ELSE (LENGTH("a1.1")) END AS is_int;
| a1.1 | is_int |
| ---- | ------ |
| a1.1 | 0 |
---
**Query #9**
SELECT "a", (LENGTH(CAST("a" AS UNSIGNED))) = CASE WHEN CAST("a" AS UNSIGNED) = 0 THEN CAST("a" AS UNSIGNED) ELSE (LENGTH("a")) END AS is_int;
| a | is_int |
| --- | ------ |
| a | 0 |
see demo
What about:
WHERE table.field = "0" or CAST(table.field as SIGNED) != 0
to test for numeric and the corrolary:
WHERE table.field != "0" and CAST(table.field as SIGNED) = 0
I have tried using the regular expressions listed above, but they do not work for the following:
SELECT '12 INCHES' REGEXP '^(-|\\+){0,1}([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$' FROM ...
The above will return 1 (TRUE), meaning the test of the string '12 INCHES' against the regular expression above, returns TRUE. It looks like a number based on the regular expression used above. In this case, because the 12 is at the beginning of the string, the regular expression interprets it as a number.
The following will return the right value (i.e. 0) because the string starts with characters instead of digits
SELECT 'TOP 10' REGEXP '^(-|\\+){0,1}([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$' FROM ...
The above will return 0 (FALSE) because the beginning of the string is text and not numeric.
However, if you are dealing with strings that have a mix of numbers and letters that begin with a number, you will not get the results you want. REGEXP will interpret the string as a valid number when in fact it is not.
This works well for VARCHAR where it begins with a number or not..
WHERE concat('',fieldname * 1) != fieldname
may have restrictions when you get to the larger NNNNE+- numbers
for me the only thing that works is:
CREATE FUNCTION IsNumeric (SIN VARCHAR(1024)) RETURNS TINYINT
RETURN SIN REGEXP '^(-|\\+){0,1}([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$';
from kevinclark all other return useless stuff for me in case of 234jk456 or 12 inches