Updating value, of a Column with PRIMARY KEY constraint - mysql

I have a MySQL table with the following schema
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(7) | NO | UNI | NULL | auto_increment |
| title | varchar(20) | NO | PRI | NULL | |
+-------+-------------+------+-----+---------+----------------+
And here is the content in it.
+----+-------+
| id | title |
+----+-------+
| 1 | a |
| 2 | b |
+----+-------+
Question: I want to interchange the values in a single query so that the table now becomes
+----+-------+
| id | title |
+----+-------+
| 1 | b |
| 2 | a |
+----+-------+
I tried: UPDATE myTable SET title = CASE id WHEN 1 THEN "b" WHEN 2 THEN "a" END;
but it gives me an error ERROR 1062 (23000): Duplicate entry 'b' for key 'PRIMARY'
What should I do?
A solution found here in one of the links seems the only way as of now but I am still looking for a better solution
START TRANSACTION;
UPDATE prime SET title = CASE id WHEN 1 THEN "$b" WHEN 2 THEN "$a" END;
UPDATE prime SET title = CASE id WHEN 1 THEN SUBSTRING(title,2) WHEN 2 THEN SUBSTRING(title,2) END;
COMMIT;

START TRANSACTION ;
UPDATE prime SET title = 'zzzzz$$$$$xxxxx!##$%' WHERE id = 1 ;
UPDATE prime SET title = 'a' WHERE id = 2 ;
UPDATE prime SET title = 'b' WHERE id = 1 ;
COMMIT ;
Comment, not related to the unique issue:
Use WHERE in your update statements, unless you want to update all the rows of the table. Your statement:
UPDATE myTable SET title = CASE id WHEN 1 THEN 'b' WHEN 2 THEN 'a' END;
(if it worked) it would also try to update all other rows (with id >= 3) with a NULL value because CASE has an implicit ELSE NULL part. Of course it would fail as the title is the primary key but in other cases, you would have undesirable effects.

with PIMARY key help to use ORDER BY title
UPDATE myTable SET title = CASE id WHEN 1 THEN "a" WHEN 2 THEN "b" ElSE title END WHERE id in (1,2) ORDER BY id DESC;
if not work change direction to ASC or ORDER BY title. And don't fogot ELSE. it doesn't work wiout it.
I use it with id, like that:
UPDATE `table1` SET `id` = CASE `id` WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 0 THEN 1 ELSE `id` END WHERE `id` IN ( 1, 2, 0 ) ORDER BY `id` DESC
all works fine,
but if update 0=>3,3=>2,2=>1 - 0 should update separatly.

Related

SQL UPDATE multiple rows at once after SELECT

My sample table:
+------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+-------+
| id | bigint(20) | YES | | NULL | |
| other_id | bigint(20) | YES | | NULL | |
| another_id | bigint(20) | YES | | NULL | |
+------------+------------+------+-----+---------+-------+
+------+----------+------------+
| id | other_id | another_id |
+------+----------+------------+
| 988 | 102 | NULL |
| 989 | 103 | NULL |
| 990 | 104 | NULL |
| 991 | 105 | NULL |
| 992 | 106 | NULL |
| 987 | 101 | NULL |
+------+----------+------------+
How would I SELECT and UPDATE the above table in one query to the effect of doing something like this for every row:
UPDATE
x
SET
another_id = 987
WHERE
id = 987
AND other_id = 101;
UPDATE
x
SET
another_id = 988
WHERE
id = 988
AND other_id = 102
I would hate to run a manual update like this for every row and would like to do it all in one go.
To me it seems that you simply want to set the value of another_id to id:
UPDATE
x
SET
another_id = id
You can provide a range of other_id values in the where clause if you need to restrict the number of rows updated:
UPDATE
x
SET
another_id = id
WHERE other_id IN (...) --list the values you want here.
Shadow is correct on his update statement. It looks like you want to carry all ID directly into the ANOTHER_ID. If you only want this to occur where the "Other_ID" is a given range, just add " where other_id between 101 and 21234" or whatever range you want it to happen for.
To see the results of what Shadow's answer would result in, change it to a simple SELECT statement to see. If it is correct, change to the update version. Example...
Select
ID,
ID AS Another_ID,
Other_ID
from
YourTable
you will get all records showing the two columns showing the ID AS The "Another_ID". It does NOT UPDATE The "Another_ID" column, just queries the value AS the result column name. Again, if you wanted only certain range of numbers, just add
where Other_ID between 101 and 21234
(or whatever value range)
Now, to see as an UPDATE command is exactly as Shadow TRIED to explain..
update YourTable set
AnotherID = ID
and ALL records get updated... If within specific range... use the same where clause as the Select.
If you want to try this without messing up production data, work with a temporary bogus table you can always delete after you are done..
insert into MyTempTable
( ID,
Another_ID,
Other_ID
)
select ID, Another_ID, Other_ID
From YourTable
where ID between 500 and 800
Now you have a test table to play with the insert table and see the impact...

Ignore unique key while updating some rows (mariaDB)

I have the following table (mariaDB):
+----+--------------+-------------+-------------+
| id | content_type | sort_number | document_id |
+----+--------------+-------------+-------------+
| 1 | text | 1 | 1 |
| 2 | table | 2 | 1 |
| 3 | text | 3 | 1 |
| 4 | image | 4 | 1 |
+----+--------------+-------------+-------------+
The combination of sort_number and document_id is unique.
Now when i want to add a new entry at position 2, I need to increment the sort_number of all entries where the sort_number >= 2 by one step.
To achieve this, i use the following query:
update `table_name` set `sort_number` = sort_number +1 where `sort_number` > ? and `document_id` = ?
But because of the unique key (sort_number and document_id) i get an error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '3' for key 'table_name_sort_number_document_id_unique'
I tired to avoid the error with SET unique_checks=0; but still get the error...
Is there a (better) way to update the sort_number in one query?
ORDER BY also applies to update query, so simply:
SET #i:=0;
UPDATE items SET disp_order=#i:=#i+1 ORDER BY item_name;
Simply start updating from the last row and traverse backwards.
I like the solution provided by Paul Spiegel. My Query looks now like this:
update `table_name` set `sort_number` = sort_number +1 where `sort_number` > ? and `document_id` = ? order by `sort_number` desc
UPDATE IGNORE is the answer
UPDATE IGNORE `table_name` set `sort_number` = sort_number +1 where `sort_number` > ? and `document_id` = ?

My mysql statement to query by primary key sometimes returns more than one row, so what happened?

My schema is this:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(10) NOT NULL,
`account_type` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
INSERT INTO user VALUES (1, "zhangsan", "premiumv"), (2, "lisi", "premiumv"), (3, "wangwu", "p"), (4, "maliu", "p"), (5, "hengqi", "p"), (6, "shuba", "p");
I have the following 6 rows in the table:
+----+-----------+--------------+
| id | user_name | account_type |
+----+-----------+--------------+
| 1 | zhangsan | premiumv |
| 2 | lisi | premiumv |
| 3 | wangwu | p |
| 4 | maliu | p |
| 5 | hengqi | p |
| 6 | shuba | p |
+----+-----------+--------------+
Here is mysql to query the table by id:
SELECT * FROM user WHERE id = floor(rand()*6) + 1;
I expect it to return one row, but the actual result is non-predictive. It either will return 0 row, 1 row or sometimes more than one row. Can somebody help clarify this? Thanks!
You're testing each row against a different random number, so sometimes multiple rows will match. To fix this, calculate the random number once in a subquery.
SELECT u.*
FROM user AS u
JOIN (SELECT floor(rand()*6) + 1 AS r) AS r
ON u.id = r.r
This method of selecting a random row from a table seems like a poor design. If there are any gaps in the id sequence (which can happen easily -- MySQL doesn't guarantee that they'll always be sequential, and deleting rows will leave gaps) then it could return an empty result. The usual way to select a random row from a table is with:
SELECT *
FROM user
ORDER BY RAND()
LIMIT 1
The WHERE part must be evaluated for each row to see if there is a match. Because of this, the rand() function is evaluated for every row. Getting an inconsistent number of rows seems reasonable.
If you add LIMIT 1 to your query, the probability of returning rows from the end diminishes.
It's because the WHERE clause floor(rand()*6) + 1 is evaluated against every rows in the table to see if the condition matches the criteria. The value could be different each time it is matched against the row from the table.
You can test with a table that has same values in the column used in WHERE clause, and you can see the result:
select * from test;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 2 | b |
| 1 | c |
| 2 | d |
| 1 | e |
| 2 | f |
+------+------+
select * from test where id = floor(rand()*2) + 1;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 2 | d |
| 1 | e |
+------+------+
In the above example, the expression floor(rand()*2) + 1 returns 1 when matching against the first row (with name = 'a') so it is included in the result set. But then it returns 2 when matching against the forth row (with name = 'd'), so it is also included in the result set even the value of id is different from the value of the first row in the result set.

MySQL: Multiple Primary Keys and Auto Increment

I'm quite new to setting up tables in MySQL and there is something I'd like to do which is a bit more advance than I'm able to do.
I have two columns as part of a composite primary key, one is a Date and an ID I would like to be an auto increment integer. For each date, I would like to reset the auto integer to 0, so something like this:
|-----------------|
|Date | ID |
|-----------------|
|2012-06-18 | 1 |
|2012-06-18 | 2 |
|2012-06-18 | 3 |
|2012-06-19 | 1 |
|2012-06-19 | 2 |
|2012-06-20 | 1 |
|-----------------|
Thanks
Here this should work.
CREATE TABLE `answer`(
`dates` DATE NOT NULL,
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`dates`,`id`)
) ENGINE=MyISAM;
It is known to cause problems with innoDB. Hope this helps you.
EDIT: RESULTS
2012-06-19 1
2012-06-19 2
2012-06-19 3
2012-07-19 1
2012-07-19 2
2012-08-19 1
On php myadmin.
Well, for me mysql does what you want automatically.
mysql> CREATE TABLE TestData(Date date not null, ID int unsigned not null auto_increment, PRIMARY KEY(Date, ID));
mysql> INSERT INTO TestData SET Date = "2012-06-18";
mysql> INSERT INTO TestData SET Date = "2012-06-18";
mysql> INSERT INTO TestData SET Date = "2012-06-18";
mysql> INSERT INTO TestData SET Date = "2012-06-19";
mysql> INSERT INTO TestData SET Date = "2012-06-19";
mysql> INSERT INTO TestData SET Date = "2012-06-20";
mysql> select * from TestData;
+------------+----+
| Date | ID |
+------------+----+
| 2012-06-18 | 1 |
| 2012-06-18 | 2 |
| 2012-06-18 | 3 |
| 2012-06-19 | 1 |
| 2012-06-19 | 2 |
| 2012-06-20 | 1 |
+------------+----+
No magic involved.
You can create a before insert trigger.
DELIMITER $$
CREATE TRIGGER `composite_auto_increment` BEFORE INSERT ON `your_table`
FOR EACH ROW
BEGIN
DECLARE max_id INT(11); -- add the appropriate column length from your table definition
SELECT ID FROM `your_table` WHERE `Date` = DATE(NOW()) INTO max_id;
SET NEW.ID = IF(ISNULL(max_id), 1, max_id + 1);
END$$
This way, if and ID already existed for the day, it gets incremented. If it didn't, it gets set to 1. Note that in this scenario, ID isn't AUTO_INCREMENT in the table definition. It just gets done by the trigger.
In trigger:
SELECT ID FROM your_table WHERE Date = DATE(NOW()) INTO max_id;
must be:
SELECT max(ID) FROM your_table WHERE Date = NEW.key_field INTO max_id;
but better is lock by key.
this is better for concurrent inserts on innodb.

MySQL Alter table, add column with unique random value

I have a table that I added a column called phone - the table also has an id set as a primary key that auto_increments. How can I insert a random value into the phone column, that won't be duplicated. The following UPDATE statement did insert random values, but not all of them unique. Also, I'm not sold I cast the phone field correctly either, but ran into issues when trying to set it as a int(11) w/ the ALTER TABLE command (mainly, it ran correctly, but when adding a row with a new phone number, the inserted value was translated into a different number).
UPDATE Ballot SET phone = FLOOR(50000000 * RAND()) + 1;
Table spec's
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| phone | varchar(11) | NO | | NULL | |
| age | tinyint(3) | NO | | NULL | |
| test | tinyint(4) | NO | | 0 | |
| note | varchar(100) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
-- tbl_name: Table
-- column_name: Column
-- chars_str: String containing acceptable characters
-- n: Length of the random string
-- dummy_tbl: Not a parameter, leave as is!
UPDATE tbl_name SET column_name = (
SELECT GROUP_CONCAT(SUBSTRING(chars_str , 1+ FLOOR(RAND()*LENGTH(chars_str)) ,1) SEPARATOR '')
FROM (SELECT 1 /* UNION SELECT 2 ... UNION SELECT n */) AS dummy_tbl
);
-- Example
UPDATE tickets SET code = (
SELECT GROUP_CONCAT(SUBSTRING('123abcABC-_$#' , 1+ FLOOR(RAND()*LENGTH('123abcABC-_$#')) ,1) SEPARATOR '')
FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS dummy_tbl
);
Try this
UPDATE Ballot SET phone = FLOOR(50000000 * RAND()) * id;
I'd tackle this by generating a (temporary) table containing the numbers in the range you need, then looping through each record in the table you wish to supply with random numbers. Pick a random element from the temp table, update the table with that, and remove it from the temp table. Not beautiful, nor fast.. but easy to develop and easy to test.