I have table
DROP TABLE IF EXISTS `slug`;
CREATE TABLE `slug` (
`slug` varchar(255) NOT NULL,
`id` int(10) unsigned NOT NULL,
`table` varchar(64) NOT NULL,
PRIMARY KEY (`slug`),
UNIQUE KEY `id_table` (`id`,`table`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `slug` (`slug`, `id`, `table`) VALUES
('aaa', 3, 'team');
I have select
SELECT `slug`, `id`, `table`
FROM `slug`
WHERE (`slug` = 0)
and result is
+------+----+-------+
| slug | id | table |
+------+----+-------+
| aaa | 3 | team |
+------+----+-------+
Condition is slug = 0, but I get row where slug is 'aaa'.
Any idea?
Mysql version 5.5.38-0ubuntu0.12.04.1
As a note, it is a bad idea to use reserved words, such as table as column names. This however is irrelevant to your question.
The important part of the query is:
WHERE slug = 0
How does MySQL handle this? What can it do? After all, you've declared slug to be a varchar() but it is being compared to an integer. Well, what MySQL does is silent conversion to the numeric type. So, it converts the value in slug to an integer.
Which integer? If slug were '123', then it would be easy. The value would be 123. Instead, the value is aaa. MySQL's conversion rules are to convert the leading "digits" of the string to a number, until are no more "digits". When there are no digits at all, the value is 0. I've put digits in quotes because it includes '-', '.', and 'e' (for exponential notation).
So, your where clause is comparing any string that doesn't start with a numeric 0 to 0. The following values should all pass: 'aaa', 'zzz', '0abc', and so on.
This is expected behavior.
In MySQL, a zero number can be equal to any string.
So when you want to compare a string with a value of an integer column, you have to cast your integer value into a string.
Related
how to apply WHERE clause on JSON column to perform a SELECT query on a table which is having two columns (id Integer, attr JSON). The JSON is nested and in the filter condition there is only one key value pair of json is allowed. This key value pair can be anywhere in the Josn.
+----+-----------------------------------------------------------------
| id | attr
|
+----+-----------------------------------------------------------------
| 1 | {"id":"0001","type":"donut","name":"Cake","ppu":0.55}
|
| 2 | {"id":"0002","type":"donut","name":"Cake","ppu":0.55,"batters":
{"batter1":100,"batter2":200}}
+----+-----------------------------------------------------------------
In MariaDB 10.2, you can use the JSON functions.
For example, if you want to SELECT all donuts from your database, you do:
SELECT * FROM t WHERE JSON_CONTAINS(attr, '"donut"', '$.type');
Note: In MariaDB, JSON functions work with all text data types (VARCHAR, TEXT etc.). The JSON type is simply an alias for LONGTEXT.
Similarly to markusjm's answer, you can select directly from the json by field like:
SELECT json_extract(attr, '$.type') FROM t;
If you are still using MySQL 5.6 (has no JSON parsing support) we can use the substring_index functions to parse json data.
Here is a working example:
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`attr` longtext COLLATE utf8_unicode_ci NOT NULL,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO products (attr, created_at)
VALUES
('{"id":"0001","type":"donut","name":"Cake","ppu":0.55}', now()),
('{"id":"0002","type":"donut","name":"Cake","ppu":0.55,"batters":{"batter1":100,"batter2":200}}', now()),
('{"id":"0003","type":"apple","name":"Apple","ppu":0.60}', now()),
('{"id":"0003","type":"orange","name":"Orange","ppu":0.65}', now());
select
substring_index(substring_index(attr, '"type":"', -1), '",', 1) AS product_type
from products
having product_type = 'donut';
My table looks like this:
| a | ts_9 | ts_11 |
|----|------|-------|
| yx | 0 | |
| xy | 0 | |
And for some reason the rows get returned when I call:
SELECT * FROM things WHERE ts_9 IN ("asdewdwedewd") OR ts_11 IN ("asdewdwedewd")
Why does that happen?
Feel free to recreate it in your db with copy paste:
CREATE TABLE `things` (
`a` char(12) NOT NULL DEFAULT '',
`ts_9` decimal(2,0) NOT NULL,
`ts_11` char(1) NOT NULL DEFAULT '',
PRIMARY KEY (`a`),
UNIQUE KEY `a` (`a`) );
INSERT INTO `things` (`a`, `ts_9`, `ts_11`) VALUES ('yx', '0', ''), ('xy', '0', '');
Because ts_9 IN ("asdewdwedewd") evaluated to true. More specifically, ts_9 has value of integer 0. According to the documentation
If all values are constants, they are evaluated according to the type
of expr
In other words, "asdewdwedewd" will be treated as an int. When mysql convert it to an int cast("asdewdwedewd" as signed integer), we get 0. Hence 0 in (0) evaluates to true.
The optimizer brings the data to the same type.
When translating a string to decimal, we get zero.
It is necessary to predefine or convert data types.
How to make sure that MySQL does not find false positives when a alphabetic character is appended to a integer value?
Turns out that given this table:
CREATE TABLE `mytable` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `mytable`
ADD PRIMARY KEY (`id`);
And this query:
SELECT * FROM `mytable` WHERE `id`='2'
That works OK and shows a result where id=2. But i noticed that a string value of '2abc' in the query also returns the that row: a false positive. I'd rather have the query fail and signal that it could not find that id in mytable..
This could be circumvented by making sure the value is a valid integer before running the query, but i'd rather keep it string-compatible for future purposes.
This is well documented in the doc
https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
Check for line
The reason for this is that there are many different strings that may
convert to the value 1, such as '1', ' 1', or '1a'.
So without explicit type cast it will convert the string to integer and in your case 2abc becomes 2 and 2=2 => true, this is called silent conversion
select '2ab'+0; => 2
but
select 'ab2'+0 => 0
There is a table:
CREATE TABLE n_dummy (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
`values` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
INSERT INTO `n_dummy` (id, `values`) VALUES
(2, '2,10'),
(10, '2,10')
(3, '7,3');
Look like this:
id | values
-----------
2 | 2,10
10 | 2,10
3 | 7,3
Fiddle included.
The first column is the integer to be searched in string from values column.
Note: Example is too simplified and looks stupid. Refactoring table structure is not the way. Only sql query with standard functions and procedures.
I want to search integer value in string, concatenated from integers with , separator.
I expects MySQL to do this with IN operator:
SELECT id
FROM n_dummy
WHERE id IN(values);
And result would be 2, 10 and 3. But MySQL returns only 2, the second and other values is not searchable with IN operator with string.
How to search integer in concatenated string using sql-query and prebuild routines?
try this brother
SELECT * FROM `n_dummy` WHERE concat(',',`values`,',') LIKE concat('%',',',`id`,',','%')
I have a MYSQL table, with 5 columns in it:
id bigint
name varchar
description varchar
slug
Can I get MySQL to automatically generate the value of slug as a 256 Bit Hash of name+description?
I am now using PHP to generate an SHA256 value of the slug prior to saving it.
Edit:
By automatic, I mean see if it's possible to change the default value of the slug field, to be a computed field that's the sha256 of name+description.
I already know how to create it as part of an insert operation.
MySQL 5.7 supports generated columns so you can define an expression, and it will be updated automatically for every row you insert or update.
CREATE TABLE IF NOT EXISTS MyTable (
id int NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
description varchar(50) NOT NULL,
slug varchar(64) AS (SHA2(CONCAT(name, description), 256)) STORED NOT NULL,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
If you use an earlier version of MySQL, you could do this with TRIGGERs:
CREATE TRIGGER MySlugIns BEFORE INSERT ON MyTable
FOR EACH ROW SET slug = SHA2(CONCAT(name, description));
CREATE TRIGGER MySlugUpd BEFORE UPDATE ON MyTable
FOR EACH ROW SET slug = SHA2(CONCAT(name, description), 256);
Beware that concat returns NULL if any one column in the input is NULL. So, to hash in a null-safe way, use concat_ws. For example:
select md5(concat_ws('', col_1, .. , col_n));
Use MySQL's CONCAT() to combine the two values and SHA2() to generate a 256 bit hash.
CREATE TABLE IF NOT EXISTS `mytable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`description` varchar(50) NOT NULL,
`slug` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `mytable` (`name`,`description`,`slug`)
VALUES ('Fred','A Person',SHA2(CONCAT(`name`,`description`),256));
SELECT * FROM `mytable`
OUTPUT:
COLUMN VALUE
id 1
name Fred
description A Person
slug ea76b5b09b0e004781b569f88fc8434fe25ae3ad17807904cfb975a3be71bd89
Try it on SQLfiddle.