MySql WHERE condition based on unique index - mysql

I have the following table groupToScore:
CREATE TABLE `groupToScore` (
`groupId` int NOT NULL,
`scoreId` varchar(255) NOT NULL,
`scoreName` varchar(255) DEFAULT NULL,
UNIQUE KEY `gToS` (`groupId`,`scoreId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
The pair (groupId, scoreId) form a unique key gTos for this table.
My question is how do I perform UPDATE using gTos in the WHERE clause?
It would look something like, UPDATE groupToScore SET scoreName = #{scoreName} WHERE gToS is equal to (groupId, scoreId).

UPDATE groupToScore SET scoreName = #{scoreName} WHERE groupId = 1 and scoreId = 1;
I believe mysql server will choose a proper index for you and you can get the one sql indexing information by put a explain at the top of the sql
explain UPDATE groupToScore SET scoreName = #{scoreName} WHERE groupId = 1 and scoreId = 1;

You can't use indices directly. The SQL engine uses the index automatically if it is applicable. Therefore, you simply query for WHERE groupId = ? AND scoreId = ?.

You cannot use the key name directly but you can use its expression. Try this:
UPDATE groupToScore SET scoreName = #{scoreName} WHERE (`groupId`,`scoreId`) = (groupId, scoreId)
--e.g for a single match
insert groupToScore values(1,3,'dd'),(1,2,'xx');
UPDATE groupToScore SET scoreName = 'aa' WHERE (`groupId`,`scoreId`) = (1, 2);
--e.g for multiple matches
UPDATE groupToScore SET scoreName = 'kk' WHERE (`groupId`,`scoreId`) in (
(1,2),
(1,3)
);

groupId = 1 and scoreId = 1
will fail to fully use your
UNIQUE KEY `gToS` (`groupId`,`scoreId`)
because of a type conflict.
When comparing a varchar column to an integer literal, the column is converted to an integer before performing the test. Do, instead
groupId = 1 and scoreId = "1"
It does not matter if you have groupId = "1"; the string will be converted to a number and the index can still be used.
The update is simply
UPDATE groupToScore SET
scoreName = #{scoreName}
WHERE groupId = 123, scoreId = "987"
And/or, change the datatype of `scoreId to be something numeric. (Of course, this assumes its values are really numbers.

Related

MySQL: Deleted a tuple (which had an ID of 10), and when I add a new entry, the ID is 11, not 10 [duplicate]

I have a MySQL table with an auto increment primary key. I deleted some rows in the middle of the table. Now I have, for example, something like this in the ID column: 12, 13, 14, 19, 20. I deleted the 15, 16, 17 and 18 rows.
I want to reassign / reset / reorder the primary key so that I have continuity, i.e. make the 19 a 15, the 20 a 16, and so on.
How can I do it?
Even though this question seems to be quite old, will post an answer for someone who reaches in here searching.
SET #count = 0;
UPDATE `users` SET `users`.`id` = #count:= #count + 1;
If the column is used as a foreign key in other tables, make sure you use ON UPDATE CASCADE instead of the default ON UPDATE NO ACTION for the foreign key relationship in those tables.
Further, in order to reset the AUTO_INCREMENT count, you can immediately issue the following statement.
ALTER TABLE `users` AUTO_INCREMENT = 1;
For MySQLs it will reset the value to MAX(id) + 1.
You could drop the primary key column and re-create it. All the ids should then be reassigned in order.
However this is probably a bad idea in most situations. If you have other tables that have foreign keys to this table then it will definitely not work.
To reset the IDs of my User table, I use the following SQL query. It's been said above that this will ruin any relationships you may have with any other tables.
ALTER TABLE `users` DROP `id`;
ALTER TABLE `users` AUTO_INCREMENT = 1;
ALTER TABLE `users` ADD `id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
You may simply use this query
alter table abc auto_increment = 1;
SET #num := 0;
UPDATE your_table SET id = #num := (#num+1);
ALTER TABLE your_table AUTO_INCREMENT =1;
I think this will do it
Or, from PhpMyAdmin, remove "AutoIncrement" flag, save, set it again and save.this resets it.
SELECT * from `user` ORDER BY `user_id`;
SET #count = 0;
UPDATE `user` SET `user_id` = #count:= #count + 1;
ALTER TABLE `user_id` AUTO_INCREMENT = 1;
if you want to order by
in phpmyadmin
note: this will work if you delete last rows not middle rows.
goto your table-> click on operations menu-> goto table options->change AUTO_INCREMENT to that no from where you want to start.
your table autoincrement start from that no.
try it.
This works - https://stackoverflow.com/a/5437720/10219008.....but if you run into an issue 'Error Code: 1265. Data truncated for column 'id' at row 1'...Then run the following. Adding ignore on the update query.
SET #count = 0;
set sql_mode = 'STRICT_ALL_TABLES';
UPDATE IGNORE web_keyword SET id = #count := (#count+1);
I had the same doubts, but could not make any changes on the table, I decided doing the following having seen my ID did not exceed the maximum number setted in the variable #count:
SET #count = 40000000;
UPDATE `users` SET `users`.`id` = #count:= #count + 1;
SET #count = 0;
UPDATE `users` SET `users`.`id` = #count:= #count + 1;
ALTER TABLE `users` AUTO_INCREMENT = 1;
The solution takes, but it's safe and it was necessary because my table owned foreign keys with data in another table.
You can remove the primary key auto increment functionality of that column, then every time you update that column run a query before hand that will count all the rows in the table, then run a loop that iterates through that row count inserting each value into the respective row, and finally run a query inserting a new row with the value of that column being the total row count plus one. This will work flawlessly and is the most absolute solution to someone trying to accomplish what you are. Here is an example of code you may use for the function:
$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("
SELECT `rank`, `field1`, `field2`, `field3`, `field4`
FROM (SELECT (#rank:=#rank+1) as `rank`, `field1`, `field2`, `field3`, `field4`
FROM (SELECT * FROM `views`) a
CROSS JOIN (SELECT #rank:=0) b
ORDER BY rank ASC) c
");
while ($row = mysql_fetch_assoc($viewsrowsdata)) {
$data[] = $row;
}
foreach ($data as $row) {
$new_field_1 = (int)$row['rank'];
$old_field_1 = (int)$row['field1'];
mysql_query("UPDATE `table` SET `field_1` = $new_field_1 WHERE `field_1` = $old_field_1");
}
mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");
Here I created an associative array which I had appended on a rank column with the query within a select query, which gave each row a rank value starting with 1. I then iterated through the associative array.
Another option would have been to get the row count, run a basic select query, get the associative array and iterate it through the same way but with an added variable that updates through each iteration. This is less flexible but will accomplish the same thing.
$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("SELECT * FROM `table`");
$updated_key = 0;
while ($row = mysql_fetch_assoc($viewsrowsdata)) {
$data[] = $row;
}
foreach ($data as $row) {
$updated_key = $updated_key + 1;
mysql_query("UPDATE `table` SET `field_1` = '$updated_key' WHERE `field_1` = '$row['field_1']'");
}
mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");
for InnoDB, do this (this will remove all records from a table, make a bakcup first):
SET #OLD_CHARACTER_SET_RESULTS=##CHARACTER_SET_RESULTS ;
SET #OLD_COLLATION_CONNECTION=##COLLATION_CONNECTION ;
SET NAMES utf8 ;
SET #OLD_UNIQUE_CHECKS=##UNIQUE_CHECKS, UNIQUE_CHECKS=0 ;
SET #OLD_FOREIGN_KEY_CHECKS=##FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 ;
SET #OLD_SQL_MODE=##SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' ;
SET #OLD_SQL_NOTES=##SQL_NOTES, SQL_NOTES=0 ;
/* ================================================= */
drop table tablename;
CREATE TABLE `tablename` (
table structure here!
) ENGINE=InnoDB AUTO_INCREMENT= ai number to reset DEFAULT CHARSET= char set here;
/* ================================================= */
SET SQL_MODE=#OLD_SQL_MODE ;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS ;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS ;
SET CHARACTER_SET_CLIENT=#OLD_CHARACTER_SET_CLIENT ;
SET CHARACTER_SET_RESULTS=#OLD_CHARACTER_SET_RESULTS ;
SET COLLATION_CONNECTION=#OLD_COLLATION_CONNECTION ;
SET SQL_NOTES=#OLD_SQL_NOTES ;
The best choice is to alter the column and remove the auto_increment attribute. Then issue another alter statement and put auto_increment back onto the column. This will reset the count to the max+1 of the current rows and thus preserve foreign key references back to this table, from other tables in your database, or any other key usage for that column.
My opinion is to create a new column called row_order. then reorder that column. I'm not accepting the changes to the primary key. As an example, if the order column is banner_position, I have done something like this, This is for deleting, updating, creating of banner position column. Call this function reorder them respectively.
public function updatePositions(){
$offers = Offer::select('banner_position')->orderBy('banner_position')->get();
$offersCount = Offer::max('banner_position');
$range = range(1, $offersCount);
$existingBannerPositions = [];
foreach($offers as $offer){
$existingBannerPositions[] = $offer->banner_position;
}
sort($existingBannerPositions);
foreach($existingBannerPositions as $key => $position){
$numbersLessThanPosition = range(1,$position);
$freshNumbersLessThanPosition = array_diff($numbersLessThanPosition, $existingBannerPositions);
if(count($freshNumbersLessThanPosition)>0) {
$existingBannerPositions[$key] = current($freshNumbersLessThanPosition);
Offer::where('banner_position',$position)->update(array('banner_position'=> current($freshNumbersLessThanPosition)));
}
}
}
Delete your id column and execute ALTER TABLE table_name ADD COLUMN id INT NOT NULL AUTO_INCREMENT UNIQUE;
You can also simply avoid using numeric IDs as Primary Key. You could use Country codes as primary id if the table holds countries information, or you could use permalinks, if it hold articles for example.
You could also simply use a random, or an MD5 value. All this options have it's own benefits, specially on IT sec. numeric IDs are easy to enumerate.

Update database sortorder column based on array value list

I need to update the sort order column based on the sequence of primary key values I get as array list from ajax call. For example I have 2 coulmns (ID, Sortorder) with values (23,1)(32,2)(21,3)(43,4), now the user from the frontend moves the 3rd row(21,3) above second row(32,2) and I get the ID array sequence as 23, 21, 32, 43 which I have to maintain. From this list, I am trying to update the sororder as per the sequence, so the database table values should look as (23,1)(32,3)(21,2)(43,4). Could you help me to get this DB update statement.
Attached the print screen for better understanding:
Java logic I have, trying to find an update sql statement to loop from an array list. I have ~1000 rows in my table and with my logic, this would trigger 1000 update queries, I don't think this is efficient. Trying to find an alternate efficient way.
Connection conn = null;
PreparedStatement pstmt = null;
conn = getConnection();
String query = "update Sortordertable set sortorder = ? where Id = ? ";
pstmt = conn.prepareStatement(query); // create a statement
String str[]=String.valueOf(s.getRecordId()).split(";");//id1;id;id3;.... list I get from ajax call
for(int i=0;i<str.length();i++)
{
pstmt.setId(1,i++); //set sortorder value as 1, 2, 3..
pstmt.setInt(2, str[i]); // In this line I want to use my array-list to update my table.
pstmt.executeUpdate(); // execute update statement
}
For the specific use case you have showed, you could use the following query, which would update only the rows with id=32 or id=23.
UPDATE t1 SET
sortorder = CASE WHEN id = 32 THEN 3 ELSE 2 END
WHERE id IN (32, 21);
This could be adapted for multiple updates if you don't update the database after each operation. but will grow with the number of operations made by the user before the update is triggered.
Edit to address comment:
If, as in the example given in your comment, you want to move the row with order 4 to the first row, you can use the following:
UPDATE t1 SET
sortorder = CASE WHEN sortorder = 4 THEN 1 ELSE sortorder + 1 END
WHERE sortorder <= 4;
I've added a where clause in that last query to illustrate that you can easily adapt this to different use case.
Here's what you can do:
DELIMITER $$
CREATE PROCEDURE `sp_update_positions`(
IN `p_positions` VARCHAR(255)
)
BEGIN
SET #positions = REPLACE(p_positions, ',', '),(');
SET #positions = CONCAT('(', #positions, ')');
DROP TEMPORARY TABLE IF EXISTS tmpPositions;
CREATE TEMPORARY TABLE tmpPositions(
`position` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`order_id` int(10) unsigned NOT NULL
);
SET #insertString = CONCAT('INSERT INTO tmpPositions (`order_id`) VALUES ', #positions);
PREPARE insertStatement FROM #insertString;
EXECUTE insertStatement;
DEALLOCATE PREPARE insertStatement;
UPDATE orders
INNER JOIN tmpPositions ON order_id = orders.id
SET orders.Sortorder = tmpPositions.position;
SELECT ROW_COUNT() AS rowCount;
END$$
DELIMITER ;
Then you call it like this:
CALL sp_update_positions('24,23,21,22');

mysql REPLACE INTO and optional values IFNULL

I'm trying to do something like this inside of a stored procedure:
REPLACE INTO mytable
SET myid = `IDvalue`, mytitle = `sMyTitle`, myoptionalvalue = IFNULL(`sMyOptValue`, myoptionalvalue);
But not seems to work, any idea how to do this?
Thanks!
The REPLACE INTO syntax works exactly like INSERT INTO except that any old rows with the same primary or unique key is automaticly deleted before the new row is inserted.
This means that instead of a WHERE clause, you should add the primary key to the values beeing replaced to limit your update.
REPLACE INTO myTable (
myPrimaryKey,
myColumn1,
myColumn2
) VALUES (
100,
'value1',
'value2'
);
...will provide the same result as...
UPDATE myTable
SET myColumn1 = 'value1', myColumn2 = 'value2'
WHERE myPrimaryKey = 100;

MySQL, how update string field by concatenating to it?

Question is how to update string field by concatenating to it?
Here is code for creating database and adding one row:
CREATE DATABASE my_db;
USE my_db;
CREATE TABLE IF NOT EXISTS my_table(
article_id INTEGER unsigned,
revision_ids VARCHAR(10),
PRIMARY KEY (article_id)
);
INSERT INTO my_table (article_id, revision_ids) VALUES (1, "154");
I need to write code which concatenate string to revision_ids field. For example, I need to concatenate ", 999" to "154", so I will get "154, 999". My version of code does not work:
UPDATE my_table SET revision_ids = CONCAT((SELECT revision_ids FROM my_table WHERE article_id = 1), ", ", "999") WHERE article_id = 1;
How to do it?
There is one important condition. Theoretically this concatenation could do several scripts, so it is important that while our updating nobody can change the field value.
This should be all you need:
UPDATE my_table SET revision_ids = CONCAT(revision_ids, ", ", "999") WHERE article_id = 1;
I gues you can do this directly,
UPDATE my_table
SET revision_ids = CONCAT(revision_ids, ', ', '999')
WHERE article_id = 1;
Use:
UPDATE my_table SET revision_ids = CONCAT(revision_ids, ", 999") WHERE article_id = 1;

Reorder / reset auto increment primary key

I have a MySQL table with an auto increment primary key. I deleted some rows in the middle of the table. Now I have, for example, something like this in the ID column: 12, 13, 14, 19, 20. I deleted the 15, 16, 17 and 18 rows.
I want to reassign / reset / reorder the primary key so that I have continuity, i.e. make the 19 a 15, the 20 a 16, and so on.
How can I do it?
Even though this question seems to be quite old, will post an answer for someone who reaches in here searching.
SET #count = 0;
UPDATE `users` SET `users`.`id` = #count:= #count + 1;
If the column is used as a foreign key in other tables, make sure you use ON UPDATE CASCADE instead of the default ON UPDATE NO ACTION for the foreign key relationship in those tables.
Further, in order to reset the AUTO_INCREMENT count, you can immediately issue the following statement.
ALTER TABLE `users` AUTO_INCREMENT = 1;
For MySQLs it will reset the value to MAX(id) + 1.
You could drop the primary key column and re-create it. All the ids should then be reassigned in order.
However this is probably a bad idea in most situations. If you have other tables that have foreign keys to this table then it will definitely not work.
To reset the IDs of my User table, I use the following SQL query. It's been said above that this will ruin any relationships you may have with any other tables.
ALTER TABLE `users` DROP `id`;
ALTER TABLE `users` AUTO_INCREMENT = 1;
ALTER TABLE `users` ADD `id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
You may simply use this query
alter table abc auto_increment = 1;
SET #num := 0;
UPDATE your_table SET id = #num := (#num+1);
ALTER TABLE your_table AUTO_INCREMENT =1;
I think this will do it
Or, from PhpMyAdmin, remove "AutoIncrement" flag, save, set it again and save.this resets it.
SELECT * from `user` ORDER BY `user_id`;
SET #count = 0;
UPDATE `user` SET `user_id` = #count:= #count + 1;
ALTER TABLE `user_id` AUTO_INCREMENT = 1;
if you want to order by
in phpmyadmin
note: this will work if you delete last rows not middle rows.
goto your table-> click on operations menu-> goto table options->change AUTO_INCREMENT to that no from where you want to start.
your table autoincrement start from that no.
try it.
This works - https://stackoverflow.com/a/5437720/10219008.....but if you run into an issue 'Error Code: 1265. Data truncated for column 'id' at row 1'...Then run the following. Adding ignore on the update query.
SET #count = 0;
set sql_mode = 'STRICT_ALL_TABLES';
UPDATE IGNORE web_keyword SET id = #count := (#count+1);
I had the same doubts, but could not make any changes on the table, I decided doing the following having seen my ID did not exceed the maximum number setted in the variable #count:
SET #count = 40000000;
UPDATE `users` SET `users`.`id` = #count:= #count + 1;
SET #count = 0;
UPDATE `users` SET `users`.`id` = #count:= #count + 1;
ALTER TABLE `users` AUTO_INCREMENT = 1;
The solution takes, but it's safe and it was necessary because my table owned foreign keys with data in another table.
You can remove the primary key auto increment functionality of that column, then every time you update that column run a query before hand that will count all the rows in the table, then run a loop that iterates through that row count inserting each value into the respective row, and finally run a query inserting a new row with the value of that column being the total row count plus one. This will work flawlessly and is the most absolute solution to someone trying to accomplish what you are. Here is an example of code you may use for the function:
$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("
SELECT `rank`, `field1`, `field2`, `field3`, `field4`
FROM (SELECT (#rank:=#rank+1) as `rank`, `field1`, `field2`, `field3`, `field4`
FROM (SELECT * FROM `views`) a
CROSS JOIN (SELECT #rank:=0) b
ORDER BY rank ASC) c
");
while ($row = mysql_fetch_assoc($viewsrowsdata)) {
$data[] = $row;
}
foreach ($data as $row) {
$new_field_1 = (int)$row['rank'];
$old_field_1 = (int)$row['field1'];
mysql_query("UPDATE `table` SET `field_1` = $new_field_1 WHERE `field_1` = $old_field_1");
}
mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");
Here I created an associative array which I had appended on a rank column with the query within a select query, which gave each row a rank value starting with 1. I then iterated through the associative array.
Another option would have been to get the row count, run a basic select query, get the associative array and iterate it through the same way but with an added variable that updates through each iteration. This is less flexible but will accomplish the same thing.
$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("SELECT * FROM `table`");
$updated_key = 0;
while ($row = mysql_fetch_assoc($viewsrowsdata)) {
$data[] = $row;
}
foreach ($data as $row) {
$updated_key = $updated_key + 1;
mysql_query("UPDATE `table` SET `field_1` = '$updated_key' WHERE `field_1` = '$row['field_1']'");
}
mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");
for InnoDB, do this (this will remove all records from a table, make a bakcup first):
SET #OLD_CHARACTER_SET_RESULTS=##CHARACTER_SET_RESULTS ;
SET #OLD_COLLATION_CONNECTION=##COLLATION_CONNECTION ;
SET NAMES utf8 ;
SET #OLD_UNIQUE_CHECKS=##UNIQUE_CHECKS, UNIQUE_CHECKS=0 ;
SET #OLD_FOREIGN_KEY_CHECKS=##FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 ;
SET #OLD_SQL_MODE=##SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' ;
SET #OLD_SQL_NOTES=##SQL_NOTES, SQL_NOTES=0 ;
/* ================================================= */
drop table tablename;
CREATE TABLE `tablename` (
table structure here!
) ENGINE=InnoDB AUTO_INCREMENT= ai number to reset DEFAULT CHARSET= char set here;
/* ================================================= */
SET SQL_MODE=#OLD_SQL_MODE ;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS ;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS ;
SET CHARACTER_SET_CLIENT=#OLD_CHARACTER_SET_CLIENT ;
SET CHARACTER_SET_RESULTS=#OLD_CHARACTER_SET_RESULTS ;
SET COLLATION_CONNECTION=#OLD_COLLATION_CONNECTION ;
SET SQL_NOTES=#OLD_SQL_NOTES ;
The best choice is to alter the column and remove the auto_increment attribute. Then issue another alter statement and put auto_increment back onto the column. This will reset the count to the max+1 of the current rows and thus preserve foreign key references back to this table, from other tables in your database, or any other key usage for that column.
My opinion is to create a new column called row_order. then reorder that column. I'm not accepting the changes to the primary key. As an example, if the order column is banner_position, I have done something like this, This is for deleting, updating, creating of banner position column. Call this function reorder them respectively.
public function updatePositions(){
$offers = Offer::select('banner_position')->orderBy('banner_position')->get();
$offersCount = Offer::max('banner_position');
$range = range(1, $offersCount);
$existingBannerPositions = [];
foreach($offers as $offer){
$existingBannerPositions[] = $offer->banner_position;
}
sort($existingBannerPositions);
foreach($existingBannerPositions as $key => $position){
$numbersLessThanPosition = range(1,$position);
$freshNumbersLessThanPosition = array_diff($numbersLessThanPosition, $existingBannerPositions);
if(count($freshNumbersLessThanPosition)>0) {
$existingBannerPositions[$key] = current($freshNumbersLessThanPosition);
Offer::where('banner_position',$position)->update(array('banner_position'=> current($freshNumbersLessThanPosition)));
}
}
}
Delete your id column and execute ALTER TABLE table_name ADD COLUMN id INT NOT NULL AUTO_INCREMENT UNIQUE;
You can also simply avoid using numeric IDs as Primary Key. You could use Country codes as primary id if the table holds countries information, or you could use permalinks, if it hold articles for example.
You could also simply use a random, or an MD5 value. All this options have it's own benefits, specially on IT sec. numeric IDs are easy to enumerate.