I'm really confused here. Running the following query:
SELECT * FROM `articles` WHERE `form` = 'Depotplåster' AND `size` = 5
returns rows that also start with "5", despite me neither using LIKE nor a % wildcard operator. How come?
The size field is of type VARCHAR.
That is because you're using comparison between numeric and varchar data. MySQL will implicitly convert your column to double, resulting in 5. See this simple test data:
mysql> select * from test;
+-----------------+
| name |
+-----------------+
| 5 |
| 5 and some crap |
+-----------------+
2 rows in set (0.00 sec)
Now, "good" way: compare strings:
mysql> select * from test where name = '5';
+------+
| name |
+------+
| 5 |
+------+
1 row in set (0.00 sec)
And "bad" way: compare integers:
mysql> select * from test where name = 5;
+-----------------+
| name |
+-----------------+
| 5 |
| 5 and some crap |
+-----------------+
2 rows in set, 1 warning (0.05 sec)
-and here is your reason:
+---------+------+-----------------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '5 and some crap' |
+---------+------+-----------------------------------------------------+
1 row in set (0.00 sec)
Finally, to understand, why is it so:
SELECT
CAST('5' AS DECIMAL) AS 5d,
CAST('5 and some crap' AS DECIMAL) AS 5sd,
CAST('5' AS DECIMAL) = CAST('5 and some crap' AS DECIMAL) AS areEqual;
Will result in:
+----+-----+----------+
| 5d | 5sd | areEqual |
+----+-----+----------+
| 5 | 5 | 1 |
+----+-----+----------+
1 row in set (0.00 sec)
-as you can see, non-significant part was just truncated (as mentioned in warning message above)
SELECT * FROM `articles` WHERE `form` = 'Depotplåster' AND `size` = '5'
-- this will compare the string 'size' with the string '5'
SELECT * FROM `articles` WHERE `form` = 'Depotplåster' AND `size` = 5
-- this will convert string 'size' to integer and then compare with the integer 5
The conversion of string to integer looks for ints i nthe beginning of the string, and takes the largest integer until the first non-numeric character.
select '5s4'=5, 's5'=5, '5'=5 -- =>1,0,1
SELECT *
FROM `articles`
WHERE `form` = 'Depotplåster'
AND `size` = '5'
You should quote 5 because MySQL convert string from the table to int without quotes.
I have a mysql table with utf8_general_ci encoding where I keep data in different languages mostly English, Turkish, Farsi, etc.
The problem is that the sql statement:
SELECT * FROM `qkw` WHERE `eword` = 'turk'
returns rows with both "turk & türk" values as result.
I have the same problem with indexing which treats ü & u the same. Is this a bug in Mysql or should I use a different encoding? Any suggestions?
Thanks
The different collations are documented here, including the effect you're seeing;
To further illustrate, the following equalities hold in both utf8_general_ci and utf8_unicode_ci (for the effect this has in comparisons or when doing searches, see Section 10.1.7.8, “Examples of the Effect of Collation”):
Ä = A
Ö = O
Ü = U
If you don't want that, you can choose a collation from that list that does not see them as equivalent, for example utf8_swedish_ci.
Your best bet would probably be to use the utf8_turkish_ci collation.
It will distinguish between 'u' and 'ü' as you wish. It is (_ci suffix) a case insensitive collation:
create table t (v varchar(255)
character set utf8
collate utf8_turkish_ci);
insert into t values ("turk"), ("türk"), ("top"), ("twin");
mysql> select * from t order by v;
+-------+
| v |
+-------+
| türk |
| top |
| turk |
| twin |
+-------+
mysql> select * from t where v = "turk";
+------+
| v |
+------+
| turk |
+------+
mysql> select * from t where v = "TURK";
+------+
| v |
+------+
| turk |
+------+
Being based on simply comparing the binary code of each character, Using utf8_bin will produce slightly different results. Not only it will be case sensitive, but the ordering will be different:
mysql> alter table t change column v v varchar(255) collate utf8_bin;
Query OK, 4 rows affected (0.24 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from t order by v;
+-------+
| v |
+-------+
| top |
| turk |
| twin |
| türk |
+-------+
4 rows in set (0.00 sec)
mysql> select * from t where v = "turk";
+------+
| v |
+------+
| turk |
+------+
1 row in set (0.00 sec)
mysql> select * from t where v = "TURK";
Empty set (0.00 sec)
When i write this query
SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id = 260;
return
title city
--------------- ------
Butterfly world Mohali
and when i write
SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id = '260abcxyz';
return
title city
--------------- ------
Butterfly world Mohali
see the second one is wrong. i want to get the value only IF ID EXACTLY MATCH ... how to solve this. thanks for your help.
You could convert the id to a string so the comparison will be done exactly. You could use LIKE to cause an implicit conversion
SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id LIKE '260abcxyz';
or alternative, you can perform the cast explicitly
SELECT cd.title, cd.city FROM coupon_detail cd WHERE CAST(cd.id AS CHAR) = '260abcxyz';
However, if all your IDs are integers, it's probably more appropriate to check these values before you try to query the database. (If the ID you're querying with isn't a number, there can't be a match anyway.)
This is happening because of Type Conversion in MySql. My sql treats '260abcxyz' as an integer in your query and AFAIK because first char is number MySql casts it to a number and it becomes only 260 and that's why you are getting result. If you write character first like 'abcxyz260' it will successes the comparison.
More explanation available here: Type Conversion in MySql
As a solution: you should take care that only numbers are passed in comparison and not combination of string and number.
Well, you should not use quote if the id is INT. The way MySQL does is if you use String to compare a INT, it uses just the valid initial integer from start.
see this:
mysql> desc coupon_detail;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| title | varchar(100) | YES | | NULL | |
| city | varchar(100) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.04 sec)
mysql> SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id = 'ff260';
Empty set, 1 warning (0.00 sec)
mysql> SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id = '260ff';
+-----------------+--------+
| title | city |
+-----------------+--------+
| Butterfly world | Mohali |
+-----------------+--------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id = 260;
+-----------------+--------+
| title | city |
+-----------------+--------+
| Butterfly world | Mohali |
+-----------------+--------+
1 row in set (0.00 sec)
mysql> SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id = 26011;
Empty set (0.00 sec)
The idea is not to mix numbers and aphabets to compare a INT column. If you have to you may consider
converting ID column to VARCHAR. In that case auto-increment may go away.
have application logic to query only the strings that has numerics.
use like instead = because if the string is too long than exact match won't work in that way
SELECT cd.title, cd.city FROM coupon_detail cd WHERE cd.id LIKE '260abcxyz';
I have a MySQL table with coordinates, the column names are X and Y. Now I want to swap the column values in this table, so that X becomes Y and Y becomes X. The most apparent solution would be renaming the columns, but I don't want to make structure changes since I don't necessarily have permissions to do that.
Is this possible to do with UPDATE in some way? UPDATE table SET X=Y, Y=X obviously won't do what I want.
Edit: Please note that my restriction on permissions, mentioned above, effectively prevents the use of ALTER TABLE or other commands that change the table/database structure. Renaming columns or adding new ones are unfortunately not options.
I just had to deal with the same and I'll summarize my findings.
The UPDATE table SET X=Y, Y=X approach obviously doesn't work, as it'll just set both values to Y.
Here's a method that uses a temporary variable. Thanks to Antony from the comments of http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ for the "IS NOT NULL" tweak. Without it, the query works unpredictably. See the table schema at the end of the post. This method doesn't swap the values if one of them is NULL. Use method #3 that doesn't have this limitation.
UPDATE swap_test SET x=y, y=#temp WHERE (#temp:=x) IS NOT NULL;
This method was offered by Dipin in, yet again, the comments of http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/. I think it’s the most elegant and clean solution. It works with both NULL and non-NULL values.
UPDATE swap_test SET x=(#temp:=x), x = y, y = #temp;
Another approach I came up with that seems to work:
UPDATE swap_test s1, swap_test s2 SET s1.x=s1.y, s1.y=s2.x WHERE s1.id=s2.id;
Essentially, the 1st table is the one getting updated and the 2nd one is used to pull the old data from.
Note that this approach requires a primary key to be present.
This is my test schema:
CREATE TABLE `swap_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`x` varchar(255) DEFAULT NULL,
`y` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `swap_test` VALUES ('1', 'a', '10');
INSERT INTO `swap_test` VALUES ('2', NULL, '20');
INSERT INTO `swap_test` VALUES ('3', 'c', NULL);
You could take the sum and subtract the opposing value using X and Y
UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y;
Here is a sample test (and it works with negative numbers)
mysql> use test
Database changed
mysql> drop table if exists swaptest;
Query OK, 0 rows affected (0.03 sec)
mysql> create table swaptest (X int,Y int);
Query OK, 0 rows affected (0.12 sec)
mysql> INSERT INTO swaptest VALUES (1,2),(3,4),(-5,-8),(-13,27);
Query OK, 4 rows affected (0.08 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM swaptest;
+------+------+
| X | Y |
+------+------+
| 1 | 2 |
| 3 | 4 |
| -5 | -8 |
| -13 | 27 |
+------+------+
4 rows in set (0.00 sec)
mysql>
Here is the swap being performed
mysql> UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y;
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4 Changed: 4 Warnings: 0
mysql> SELECT * FROM swaptest;
+------+------+
| X | Y |
+------+------+
| 2 | 1 |
| 4 | 3 |
| -8 | -5 |
| 27 | -13 |
+------+------+
4 rows in set (0.00 sec)
mysql>
Give it a Try !!!
The following code works for all scenarios in my quick testing:
UPDATE swap_test
SET x=(#temp:=x), x = y, y = #temp
UPDATE table SET X=Y, Y=X will do precisely what you want (edit: in PostgreSQL, not MySQL, see below). The values are taken from the old row and assigned to a new copy of the same row, then the old row is replaced. You do not have to resort to using a temporary table, a temporary column, or other swap tricks.
#D4V360: I see. That is shocking and unexpected. I use PostgreSQL and my answer works correctly there (I tried it). See the PostgreSQL UPDATE docs (under Parameters, expression), where it mentions that expressions on the right hand side of SET clauses explicitly use the old values of columns. I see that the corresponding MySQL UPDATE docs contain the statement "Single-table UPDATE assignments are generally evaluated from left to right" which implies the behaviour you describe.
Good to know.
Ok, so just for fun, you could do this! (assuming you're swapping string values)
mysql> select * from swapper;
+------+------+
| foo | bar |
+------+------+
| 6 | 1 |
| 5 | 2 |
| 4 | 3 |
+------+------+
3 rows in set (0.00 sec)
mysql> update swapper set
-> foo = concat(foo, "###", bar),
-> bar = replace(foo, concat("###", bar), ""),
-> foo = replace(foo, concat(bar, "###"), "");
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from swapper;
+------+------+
| foo | bar |
+------+------+
| 1 | 6 |
| 2 | 5 |
| 3 | 4 |
+------+------+
3 rows in set (0.00 sec)
A nice bit of fun abusing the left-to-right evaluation process in MySQL.
Alternatively, just use XOR if they're numbers. You mentioned coordinates, so do you have lovely integer values, or complex strings?
Edit: The XOR stuff works like this by the way:
update swapper set foo = foo ^ bar, bar = foo ^ bar, foo = foo ^ bar;
I believe have a intermediate exchange variable is the best practice in such way:
update z set c1 = #c := c1, c1 = c2, c2 = #c
First, it works always; second, it works regardless of data type.
Despite of Both
update z set c1 = c1 ^ c2, c2 = c1 ^ c2, c1 = c1 ^ c2
and
update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2
are working usually, only for number data type by the way, and it is your responsibility to prevent overflow, you can not use XOR between signed and unsigned, you also can not use sum for overflowing possibility.
And
update z set c1 = c2, c2 = #c where #c := c1
is not working
if c1 is 0 or NULL or zero length string or just spaces.
We need change it to
update z set c1 = c2, c2 = #c where if((#c := c1), true, true)
Here is the scripts:
mysql> create table z (c1 int, c2 int)
-> ;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into z values(0, 1), (-1, 1), (pow(2, 31) - 1, pow(2, 31) - 2)
-> ;
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| -1 | 1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.02 sec)
mysql> update z set c1 = c1 ^ c2, c2 = c1 ^ c2, c1 = c1 ^ c2;
ERROR 1264 (22003): Out of range value for column 'c1' at row 2
mysql> update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2;
ERROR 1264 (22003): Out of range value for column 'c1' at row 3
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| 1 | -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.02 sec)
mysql> update z set c1 = c2, c2 = #c where #c := c1;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| -1 | 1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.00 sec)
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 1 | 0 |
| 1 | -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.00 sec)
mysql> update z set c1 = #c := c1, c1 = c2, c2 = #c;
Query OK, 3 rows affected (0.02 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| -1 | 1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.00 sec)
mysql>update z set c1 = c2, c2 = #c where if((#c := c1), true, true);
Query OK, 3 rows affected (0.02 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 1 | 0 |
| 1 | -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.00 sec)
ALTER TABLE table ADD COLUMN tmp;
UPDATE table SET tmp = X;
UPDATE table SET X = Y;
UPDATE table SET Y = tmp;
ALTER TABLE table DROP COLUMN tmp;
Something like this?
Edit: About Greg's comment:
No, this doesn't work:
mysql> select * from test;
+------+------+
| x | y |
+------+------+
| 1 | 2 |
| 3 | 4 |
+------+------+
2 rows in set (0.00 sec)
mysql> update test set x=y, y=x;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from test;
+------+------+
| x | y |
+------+------+
| 2 | 2 |
| 4 | 4 |
+------+------+
2 rows in set (0.00 sec)
Two alternatives
1. Use a temporary table
2. Investigate
the XOR algorithm
As other answers point out, a simple swap won't work with MySQL because it caches the value of column 1 immediately before processing column 2, resulting in both columns being set to the value of column 2.
Given that the order of operations is not guaranteed in MySQL, using a temporary variable is also not reliable.
The only safe way to swap two columns without modifying the table structure is with an inner join, which requires a primary key (id in this case).
UPDATE mytable t1, mytable t2
SET t1.column1 = t1.column2,
t1.column2 = t2.column1
WHERE t1.id = t2.id;
This will work without any issues.
I've not tried it but
UPDATE tbl SET #temp=X, X=Y, Y=#temp
Might do it.
Mark
This surely works! I've just needed it to swap Euro and SKK price columns. :)
UPDATE tbl SET X=Y, Y=#temp where #temp:=X;
The above will not work (ERROR 1064 (42000): You have an error in your SQL syntax)
In SQL Server, you can use this query:
update swaptable
set col1 = t2.col2,
col2 = t2.col1
from swaptable t2
where id = t2.id
Assuming you have signed integers in your columns, you may need to use CAST(a ^ b AS SIGNED), since the result of the ^ operator is an unsigned 64-bit integer in MySQL.
In case it helps anyone, here's the method I used to swap the same column between two given rows:
SELECT BIT_XOR(foo) FROM table WHERE key = $1 OR key = $2
UPDATE table SET foo = CAST(foo ^ $3 AS SIGNED) WHERE key = $1 OR key = $2
where $1 and $2 are the keys of two rows and $3 is the result of the first query.
You could change column names, but this is more of a hack. But be cautious of any indexes that may be on these columns
Table name is customer.
fields are a and b, swap a value to b;.
UPDATE customer SET a=(#temp:=a), a = b, b = #temp
I checked this is working fine.
You can apply below query, It worked perfect for me.
Table name: studentname
only single column available: name
update studentnames
set names = case names
when "Tanu" then "dipan"
when "dipan" then "Tanu"
end;
or
update studentnames
set names = case names
when "Tanu" then "dipan"
else "Tanu"
end;
Swapping of column values using single query
UPDATE my_table SET a=#tmp:=a, a=b, b=#tmp;
cheers...!
I had to just move value from one column to the other (like archiving) and reset the value of the original column.
The below (reference of #3 from accepted answer above) worked for me.
Update MyTable set X= (#temp:= X), X = 0, Y = #temp WHERE ID= 999;
CREATE TABLE Names
(
F_NAME VARCHAR(22),
L_NAME VARCHAR(22)
);
INSERT INTO Names VALUES('Ashutosh', 'Singh'),('Anshuman','Singh'),('Manu', 'Singh');
UPDATE Names N1 , Names N2 SET N1.F_NAME = N2.L_NAME , N1.L_NAME = N2.F_NAME
WHERE N1.F_NAME = N2.F_NAME;
SELECT * FROM Names;
This example swaps start_date and end_date for records where the dates are the wrong way round (when performing ETL into a major rewrite, I found some start dates later than their end dates. Down, bad programmers!).
In situ, I'm using MEDIUMINTs for performance reasons (like Julian days, but having a 0 root of 1900-01-01), so I was OK doing a condition of WHERE mdu.start_date > mdu.end_date.
The PKs were on all 3 columns individually (for operational / indexing reasons).
UPDATE monitor_date mdu
INNER JOIN monitor_date mdc
ON mdu.register_id = mdc.register_id
AND mdu.start_date = mdc.start_date
AND mdu.end_date = mdc.end_date
SET mdu.start_date = mdu.end_date, mdu.end_date = mdc.start_date
WHERE mdu.start_date > mdu.end_date;
Let's say you want to swap the value of first and last name in tb_user.
The safest would be:
Copy tb_user. So you will have 2 tables: tb_user and tb_user_copy
Use UPDATE INNER JOIN query
UPDATE tb_user a
INNER JOIN tb_user_copy b
ON a.id = b.id
SET a.first_name = b.last_name, a.last_name = b.first_name
if you want to swap all the columns where x is to y and y to x; use this query.
UPDATE table_name SET column_name = CASE column_name WHERE 'value of col is x' THEN 'swap it to y' ELSE 'swap it to x' END;
Let's imagine this table and let's try to swap the m and f from the 'sex' table:
id
name
sex
salary
1
A
m
2500
2
B
f
1500
3
C
m
5500
4
D
f
500
UPDATE sex
SET sex = CASE sex
WHEN 'm' THEN 'f'
ELSE 'm'
END;
So the updated table becomes:
id
name
sex
salary
1
A
f
2500
2
B
m
1500
3
C
f
5500
4
D
m
500