Strange MySQL IN behavior - mysql

OK this is not a question 100%.
I got a big headache today with this and just wanted to know if anyone else had this problem.
Let's say we have a table named "products":
ID | product_code
4 | 5201279007942_AKYRO
If we run this query:
SELECT *
FROM `products`
WHERE product_code
IN ( '5201279007942' )
Mysql will return 0 rows as expected.
If we run this query:
SELECT *
FROM `products`
WHERE product_code
IN ( 5201279007942 )
Mysql will return the above row.
So the "IN" query will act as a "LIKE" query.
Is this behavior normal? Am i missing something here?

see the output. There is a difference between string ant int
SELECT
'5201279007942_AKYRO' IN ('5201279007942') AS string_with_string
, '5201279007942_AKYRO' IN (5201279007942) AS string_with_num
, CAST( '5201279007942_AKYRO' AS UNSIGNED) IN (5201279007942) AS cast_to_int_with_num;
sample
mysql> SELECT
-> '5201279007942_AKYRO' IN ('5201279007942') AS string_with_string
-> , '5201279007942_AKYRO' IN (5201279007942) AS string_with_num
-> , CAST( '5201279007942_AKYRO' AS UNSIGNED) IN (5201279007942) AS cast_to_int_with_num;
+--------------------+-----------------+----------------------+
| string_with_string | string_with_num | cast_to_int_with_num |
+--------------------+-----------------+----------------------+
| 0 | 1 | 1 |
+--------------------+-----------------+----------------------+
1 row in set, 2 warnings (0,00 sec)
mysql>

Related

MySQL - where condition with small int

I have tasks table with status of type smallint(5). It has 0 or 1 or 2 values.
When i run this query, it give me result
SELECT * FROM `tasks` WHERE `status` = 'EMPTY'
There is no EMPTY value in status, then how i can get result?
Impicit data conversion is happening for example
select cast('empty' as int);
+----------------------+
| cast('empty' as int) |
+----------------------+
| 0 |
+----------------------+
1 row in set, 1 warning (0.00 sec)
I'm afraid it's up to you to make sure you compare like with like.
If the values can only be 0, 1 or 2 then this should give all rows with a 0 status:
SELECT *
FROM tasks
WHERE status = 0
If there are NULL values then you can get those by changing the WHERE to
WHERE status IS NULL
NULL is not the same as 0, so to get both, you would need to do:
WHERE status IS NULL OR status = 0

Extracting numerical values from mySQL column string value

I have a "person" column in a mySQL database that represents the age and weight of a person as a string separated by a comma.
Example:
"24,175"
I want to be able to separate and extract those values and cast them as numbers.
Example: turn "24,175" to
24 as age
175 as weight
So that I can write a query similar to the following
SELECT person
FROM TABLE
WHERE age>140 OR weight>1000
I want to be able to check for values that are not possible. i.e age>140 OR weight >1000.
I cannot modify the table/environment I'm working with
I only have access to queries.
I'm thinking about solving it this way
find the index where the comma exists. CHARINDEX(',',person)
Split the string into substrings using LEFT , RIGHT, CAST and CHARINDEX(',',person)
Cast age substring and weight substring to numbers using CAST(age AS INT) CAST(weight AS INT)
SELECT person
FROM TABLE
WHERE CAST(LEFT(person,CHARINDEX(',',person) AS INT)>150 OR CAST(RIGHT(person,CHARINDEX(',',person) AS INT) >1000
If I did anything wrong please correct me.
Are all the functions usable/supported by mySQL? (RIGHT, LEFT, CHARINDEX) Will this work?
Exception: Another value for this column could be "unknown". Will this cause errors if we're trying to check for the index of , if it doesn't exist in the string? Is there a way to include "unknown" cases in the result and have it output a message of "error, person not recognized"
you can also split is with SUBSTR_INDEX like this:
MariaDB [yourschema]> SELECT * FROM spliit;
+----+--------+
| id | d |
+----+--------+
| 1 | 24,175 |
+----+--------+
1 row in set (0.03 sec)
MariaDB [yourschema]> SELECT
-> SUBSTRING_INDEX(d, ',', 1) AS age
-> , SUBSTRING_INDEX(d, ',', -1) AS weight
->
-> FROM spliit;
+------+--------+
| age | weight |
+------+--------+
| 24 | 175 |
+------+--------+
1 row in set (0.00 sec)
MariaDB [yourschema]>
sample
yes, you can direct calculate with it in MySQL
MariaDB [yourschema]> SELECT
-> SUBSTRING_INDEX(d, ',', 1) + 2 AS age
-> , SUBSTRING_INDEX(d, ',', 1) * 12 AS `month`
-> , SUBSTRING_INDEX(d, ',', -1) + 3 AS weight
-> FROM spliit;
+------+-------+--------+
| age | month | weight |
+------+-------+--------+
| 26 | 288 | 178 |
+------+-------+--------+
1 row in set, 1 warning (0.03 sec)
MariaDB [yourschema]>
SELECT person
FROM TABLE
WHERE CAST(LEFT(person,LOCATE(',',person) AS INTEGER)>150 OR CAST(RIGHT(person,(LOCATE(',',person)+1) AS INTEGER) >1000
Instead of Char index use LOCATE im MqSQL
Also note the CAST function
You also can use VIRTUAL PERSITENT COLUMNS that calculate the fields automatis and you can also use a INDEX on each substr / Integer.
sample
MariaDB [yourschema]> CREATE TABLE `splitit` (
-> `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-> `d` VARCHAR(32) DEFAULT NULL,
-> age INT(11) AS (SUBSTRING_INDEX(d, ',', 1)) PERSISTENT,
-> weight INT(5) AS (SUBSTRING_INDEX(d, ',', -1)) PERSISTENT,
-> PRIMARY KEY (`id`),
-> INDEX idx_age (age),
-> INDEX idx_weight (weight)
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.79 sec)
MariaDB [yourschema]> INSERT INTO splitit (d) VALUES ('11,234'),('2,66'),('5,2');
Query OK, 3 rows affected (0.06 sec)
Records: 3 Duplicates: 0 Warnings: 0
MariaDB [yourschema]> SELECT * FROM splitit;
+----+--------+------+--------+
| id | d | age | weight |
+----+--------+------+--------+
| 1 | 11,234 | 11 | 234 |
| 2 | 2,66 | 2 | 66 |
| 3 | 5,2 | 5 | 2 |
+----+--------+------+--------+
3 rows in set (0.00 sec)
MariaDB [yourschema]>
You can do this all in the where clause:
where substring_index(person, ',', 1) + 0 > 140 or
substring_index(person, ',' -1) + 0 > 1000
Note that the + 0 does an silent conversion to integers. And, substring_index()is much more convenient than the functions in SQL Server.
You can readily incorporate this logic into a view:
create view v_table as
select t.*,
substring_index(person, ',', 1) + 0 as age,
substring_index(person, ',' -1) + 0 as weight
from table t;
If you want to filter out bad values within the view, you can use a MySQL extension and add:
having age > 140 or weight > 1000
after the from clause.

Using GROUP_CONCAT output for subquery

I'm running into a problem, I have the following line in my query
CONCAT('',
(SELECT GROUP_CONCAT(DISTINCT trips_loads_rel.load_id,'') AS x
FROM `trips_loads_rel` WHERE trips_loads_rel.trip_id = trips.Id)
) AS loads
It shows something like 8,10,27 (ie, numeric IDs), some Ids from trips_loads_rel table. It works ok. However, how can I use that output to pull matching records from other table? I mean, the line shows me all ids ok, but I need to query other table with these to pull related records. Actually I don't need these IDs, I need their matching records...
try this:
SELECT * FROM <other_table> WHERE <other_table>.load_id IN(CONCAT('',
(SELECT GROUP_CONCAT(DISTINCT trips_loads_rel.load_id,'') AS x
FROM `trips_loads_rel` WHERE trips_loads_rel.trip_id = trips.Id)
) AS loads)
It looks like it's a part of a query, not an entire one, so hard to show exact syntax, but if you want to use the values to find other rows, just don't group concat them to begin with.
Instead just use them directly doing something like;
SELECT * FROM `other_table` WHERE `other_table`.`load_id` IN
(SELECT `load_id` FROM `trips_loads_rel` WHERE `trip_id` = `trips`.`Id`)
You can use FIND_IN_SET(col, 'csv as string') function to get the desired results.
Example:
mysql> select find_in_set( 2, '11,12,13,14,15,2' );
+--------------------------------------+
| find_in_set( 2, '11,12,13,14,15,2' ) |
+--------------------------------------+
| 6 |
+--------------------------------------+
1 row in set (0.00 sec)
mysql> select find_in_set( 2, '2,11,12,13,14,15,2' );
+----------------------------------------+
| find_in_set( 2, '2,11,12,13,14,15,2' ) |
+----------------------------------------+
| 1 |
+----------------------------------------+
1 row in set (0.00 sec)
mysql> Select FIND_IN_SET( 6, '1,12,3,14,5,16,7,18,9,0,2,13,4,15,6,17,8' );
+--------------------------------------------------------------+
| FIND_IN_SET( 6, '1,12,3,14,5,16,7,18,9,0,2,13,4,15,6,17,8' ) |
+--------------------------------------------------------------+
| 15 |
+--------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>
For your query you can pass the concatenated output '8,10,27' to compare with other_table's column.
Select find_in_set( other_table.col_name, '8,10,27' );
Refer To:
MySQL String Functions: FIND_IN_SET()

Calculate difference between two datetimes in MySQL

I am storing the last login time in MySQL in, datetime-type filed. When users logs in, I want to get the difference between the last login time and the current time (which I get using NOW()).
How can I calculate it?
USE TIMESTAMPDIFF MySQL function. For example, you can use:
SELECT TIMESTAMPDIFF(SECOND, '2012-06-06 13:13:55', '2012-06-06 15:20:18')
In your case, the third parameter of TIMSTAMPDIFF function would be the current login time (NOW()). Second parameter would be the last login time, which is already in the database.
my two cents about logic:
syntax is "old date" - :"new date", so:
SELECT TIMESTAMPDIFF(SECOND, '2018-11-15 15:00:00', '2018-11-15 15:00:30')
gives 30,
SELECT TIMESTAMPDIFF(SECOND, '2018-11-15 15:00:55', '2018-11-15 15:00:15')
gives:
-40
If your start and end datetimes are on different days use TIMEDIFF.
SELECT TIMEDIFF(datetime1,datetime2)
if datetime1 > datetime2 then
SELECT TIMEDIFF("2019-02-20 23:46:00","2019-02-19 23:45:00")
gives: 24:01:00
and datetime1 < datetime2
SELECT TIMEDIFF("2019-02-19 23:45:00","2019-02-20 23:46:00")
gives: -24:01:00
I don't think the accepted answer is appropriate. For example, if the difference between last login time and current time is 8 hours then getting the difference in seconds is illogical. The correct format will be in hours, minutes and seconds. I have illustrated this as follows -
Here, I create a table login_info table to store login information of users.
CREATE TABLE login_info (
-> user_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
-> last_login DATETIME NOT NULL,
-> PRIMARY KEY (user_id)
-> );
Then I populate the table using some random values -
INSERT INTO login_info (last_login) VALUES
-> ("2021-09-22 09:32:44"),
-> ("2021-09-22 13:02:57"),
-> ("2021-09-21 23:43:21"),
-> ("2021-09-22 04:43:39"),
-> ("2021-09-22 17:23:21");
Now I calculate the difference between last_login and current_time as follows:
CREATE TABLE login_dur_in_sec AS
-> SELECT user_id,
-> TIMESTAMPDIFF(SECOND, last_login, NOW()) AS diff
-> FROM login_info;
SELECT * FROM login_dur_in_sec;
+---------+-------+
| user_id | diff |
+---------+-------+
| 1 | 28580 |
| 2 | 15967 |
| 3 | 63943 |
| 4 | 45925 |
| 5 | 343 |
+---------+-------+
CREATE TABLE hour_section AS
-> SELECT user_id,
-> FLOOR (diff / 3600) AS hour_part
-> FROM login_dur_in_sec;
CREATE TABLE minute_section AS
-> SELECT user_id,
-> FLOOR (MOD (diff, 3600)/ 60) AS minute_part
-> FROM login_dur_in_sec;
CREATE TABLE second_section AS
-> SELECT user_id,
-> MOD (MOD (diff, 3600), 60) AS second_part
-> FROM login_dur_in_sec;
CREATE TABLE login_dur AS
-> SELECT h.user_id, h.hour_part, m.minute_part, s.second_part
-> FROM hour_section AS h INNER JOIN minute_section AS m
-> ON h.user_id = m.user_id
-> INNER JOIN second_section AS s
-> ON m.user_id = s.user_id;
CREATE TABLE login_dur_trunc AS
-> SELECT user_id,
-> CONCAT (hour_part, ":", minute_part, ":", second_part) AS login_duration
-> FROM login_dur;
SELECT * FROM login_dur_trunc;
+---------+----------------+
| user_id | login_duration |
+---------+----------------+
| 1 | 8:14:46 |
| 2 | 4:44:33 |
| 3 | 18:4:9 |
| 4 | 13:3:51 |
| 5 | 0:24:9 |
+---------+----------------+
Here, the answer given by #Adi won't work always as pointed out by #CaiusJard.

mysql fake select

My purpose is: to get multiple rows from a value list,like (1,2,3,4,5),('a','b','c','anything') and so on.
mysql> select id from accounts where id in (1,2,3,4,5,6);
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 5 |
| 6 |
+----+
5 rows in set (0.00 sec)
The above sql is surely ok,but my question is:is there a way to get the same result without
specifying a table?Because my purpose here is just to propagate rows by an id_set
another example:
mysql> select now() as column1;
+---------------------+
| column1 |
+---------------------+
| 2009-06-01 20:59:33 |
+---------------------+
1 row in set (0.00 sec)
mysql>
This example propagated a single row result without specifying a table,
but how to propagate multiple rows from a string like (1,2,3,4,5,6)?
Something like this should work:
SELECT 0 as id
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
Afterwards, you can select what you need from it by giving it an alias:
SELECT *
FROM (
SELECT 0 as id
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
) `table1`
MySQL has a dummy table: DUAL. but using DUAL doesn't change anything (it's just for convenience), and certainly doesn't make this query work.
I'm sure there's a better way to achieve what you're trying to do. We might be able to help if you explain your problem.
This does not answer your question exactly, but I believe this will fix your actual problem..
SET #counter = 0;
SELECT (#counter := #counter + 1 as counter) ... rest of your query
A simple and old fashioned way is to use a table which holds consecutive values.
DROP TABLE IF EXISTS `range10`;
CREATE TABLE IF NOT EXISTS `range10` (
`id` int(11) NOT NULL,
KEY `id` (`id`)
) ENGINE=MyISAM;
INSERT INTO `range10` (`id`) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
Once installed you can write queries as shown below.
get every second row:
select * from your_data_table where id in (
SELECT id*2 as id FROM `range10` WHERE id in(
select id from `range10`
)
)
get rows from 1101 to 1111:
select * from your_data_table where id in (
SELECT id+1100 as id FROM `range10` WHERE id in(
select id from `range10`
)
)
So if you are in the need of greater ranges, then just increase the size of the consecutive values in table range10.
Querying is simple, cost are low, no stored procedure or function needed.
Note:
You can create a table with consecutive char values, too. But varying the contents would not be so easy.
One technique I've found invaluable is an "integers table", which lets you easily do all kinds of neat things including this one (xaprb has written several blog posts on this technique and the closely related "mutex table" one).
Here a way to create custom rows directly with MySQL request SELECT :
SELECT ALL *
FROM (
VALUES
ROW ('1.1', '1.2', '1.3'),
ROW ('2.1', '2.2', '2.3'),
ROW ('3.1', '3.2', '3.3')
) AS dummy (c1, c2, c3)
Gives us a table dummy:
c1 c2 c3
-------------
1.1 1.2 1.3
2.1 2.2 2.3
3.1 3.2 3.3