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` = ?
Related
Suppose a table contains data like
MariaDB [c]> select * from t2;
+-----+
| abc |
+-----+
| 1 |
| 3 |
| 5 |
+-----+
Suppose my update command is
MariaDB [c]> update t2 set abc = abc+2;
It give following error
ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY'
While the above command works fine in oracle 10g, Is it some kind of bug or what?
The following is just an illustration and trivial.
create table t2
( id int auto_increment primary key,
abc int not null,
unique key(abc)
);
insert t2(abc) values (1),(3),(5); -- 3 rows added
update t2 set abc = abc+2; -- Error Code 1062: Duplicate entry '3' for key 'abc'
The above error occurred because the update marched in the order of the primary key, also the physical ordering, and changing the 1 to a 3 violated the 3 that was already in place via the unique key. The fact that the end state would make every thing OK, ideally, doesn't keep it from failing at that moment.
To illustrate this working in this highly rigged example knowing there is no other data:
truncate table t2; -- the data wasn't modified but for the paranoid, clear and re-insert
insert t2(abc) values (1),(3),(5); -- 3 rows added
Try it bottom up (so that the Unique constraint is not violated):
update t2 set abc = abc+2 order by abc desc;
select * from t2;
+----+-----+
| id | abc |
+----+-----+
| 1 | 3 |
| 2 | 5 |
| 3 | 7 |
+----+-----+
It leverages the ability to have an order by in an update statement.
So it comes down to knowing your data and what you can get away with. Saying it worked on Oracle as you did in comments is on another db platform and with some other schema. So that is mute.
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.
I have a table with a unique index across two columns user_id and country_id
I have added a new column deleted_at so I can delete rows whilst keeping the data.
I would now like to update the unique key so that it is based on user_id, country_id and where deleted_at IS NULL. Is this possible, if so how?
+----+---------+------------+------------+
+ id | user_id | country_id | deleted_at |
+----+---------+------------+------------+
+ 2 | 3 | 1 | NULL |
+ 3 | 3 | 1 | 2012-10-16 |
| 4 | 3 | 1 | 2012-10-15 |
+----+---------+------------+------------+
Using the above as reference, rows could not be added because of id 2, however if row 2 was not set a new row could be created.
Modifying your table mytable should do the trick:
alter table mytable drop index user_country;
alter table mytable add
unique index user_country_deleted (user_id, country_id, deleted_at);
Edit: I was too quick. According to CREATE INDEX Syntax this works only for BDB storage.
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.
I have table like this:
===============
| rank | name |
===============
| 3 | john |
| 6 | bob |
| 10 | alex |
| 11 | brad |
| 12 | matt |
| 34 | luke |
| 145 | ben |
===============
(this table is an example. In reality my table consists of ~5000 rows of data).
Is there a query to reorder the rank values starting from 1 and going up so it ends up like this:
===============
| rank | name |
===============
| 1 | john |
| 2 | bob |
| 3 | alex |
| 4 | brad |
| 5 | matt |
| 6 | luke |
| 7 | ben |
===============
It would be preferable to do this in 1 or 2 queries, not 1 query for each row since my table has 5000+ rows.
EDIT: Sorry I wasn't clear. I am trying to UPDATE the values in the database.
This is a little crude but will work in a pinch.
First order your table correctly just incase
ALTER TABLE tablename ORDER BY rank
Then drop the column
ALTER TABLE tablename DROP rank
Then add it again, with auto increment
ALTER TABLE tablename ADD COLUMN rank INT NOT NULL AUTO_INCREMENT FIRST
The auto increment will take care of numbering them in order, plus you don't have to loop through each row.
Here is the solution I came up with for this problem:
1.Create a temporary table without any keys
CREATE TEMPORARY TABLE tempTable (
id INT(11) NOT NULL
)
COLLATE='latin1_swedish_ci'
ENGINE=MyISAM
ROW_FORMAT=DEFAULT;
2.Populate the temporary table with data from the original table, ordered by rank
INSERT INTO tempTable SELECT id FROM myTable ORDER BY rank;
3.Add auto-incrementing rank column, giving all rows a unique rank, counting up from 1
ALTER TABLE tempTable
ADD COLUMN `rank` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
ADD PRIMARY KEY (`rank`);
4.Update the original table with a join to the temp table, overriding the original ranks
UPDATE myTable
INNER JOIN tempTable
ON myTable.id = tempTable.id
SET myTable.rank = tempTable.rank;
5.Drop the temp table
DROP TABLE tempTable;
An alternative to a strict MySQL solution would be to loop through the rows with a scripting language. Not a great idea if you have a large table, but could be acceptable if this is a one time fix.
In PHP
$db = mysql_connect('localhost', 'user', 'password');
mysql_select_db('database', $db);
$result = mysql_query("SELECT rank
FROM myTable
ORDER BY rank");
$i = 1;
while ($row = mysql_fetch_assoc($result)) {
mysql_query("UPDATE myTable
SET rank = " . $i++ . "
WHERE rank = " . $row['rank']);
}
Note that this will only work if rank is unique and you traverse in an order.
set #a:=(select max(id) from mytable)+1;
update mytable set id=(#a:=#a+1)
order by id;
set #a := 0;
update mytable set id=(#a:=#a+1)
order by id;
simple way, work for me. easy way.