Updating multiple rows with a composite key in MySQL - mysql

I'm trying to update multiple rows in a table but due to server resources being limited I can't do it in individual UPDATE statements. I know that this can be done in a single statement by using a CASE but I'm having trouble getting this to work as the table needs to be updated using a composite key (three columns) rather than a single value.
This example works:
UPDATE myTable
SET newValue = CASE id
WHEN 1 THEN 'val1'
WHEN 2 THEN 'val2'
WHEN 3 THEN 'val3'
END,
WHERE id IN (1,2,3)
But I need something like this to work:
UPDATE myTable
SET newValue = CASE (id,x,y)
WHEN (1,1,1) THEN 'val1'
WHEN (1,1,2) THEN 'val2'
WHEN (1,1,3) THEN 'val3'
END,
WHERE id IN (1,2,3)
Is there a way to have something like this in a single UPDATE statement or am I going to have to work around the limited server resources?

Slightly refactored from your example:
UPDATE myTable
SET newValue = CASE
WHEN id=1 AND x=1 AND y=1 THEN 'val1'
WHEN id=1 AND x=1 AND y=2 THEN 'val2'
WHEN id=1 AND x=1 AND y=3 THEN 'val3'
END
WHERE id IN (1,2,3)

You could use MySQL's "REPLACE":
REPLACE INTO myTable (id,x,y,newValue)
VALUES (1,1,1,'val1'),
(1,1,2,'val2'),
(1,1,3,'val3');

Related

Fastest way to update multiple rows in a MySQL database with no unique indexes

I need to update a large number of rows in a single database, as quickly as possible. Each row has a generated "key" field which is NOT guaranteed to be unique; if multiple rows have the same key they may all be updated with the same values.
If the keys were unique, I would do this using an INSERT VALUES ON DUPLICATE KEY UPDATE method. Unfortunately, the database in question has no unique keys and I am unable to add any, so I need a different solution.
The simplest solution is to simply write a different update statement for each item and send them one after another:
UPDATE my_table SET field1=value1_1, field2=value2_2, field3=value3_3 WHERE key=1;
UPDATE my_table SET field1=value1_2, field2=value2_2, field3=value3_2 WHERE key=2;
But this would require thousands of independent queries.
Another option is to merge them all into a single query using CASE WHEN:
UPDATE my_table SET field1 =
CASE WHEN key=1 THEN value_1_1
ELSE WHEN key=2 THEN value_1_2
END ,
field2 =
CASE WHEN key=1 THEN value_2_1
ELSE WHEN key=2 THEN value_2_2
END ,
field3 =
CASE WHEN key=1 THEN value_3_1
ELSE WHEN key=2 THEN value_3_2
END
Finally, there's this method of effectively building a fake temporary table and joining it together with the real one:
UPDATE my_table t1 JOIN (
(SELECT 1 AS key, value_1_1 AS field1_new, value_2_1 AS field2_new, value_3_1 AS field3_new) UNION ALL
(SELECT 2, value_1_2, value_2_2, value_3_2)
) vals ON t1.key = vals.key SET field1 = field1_new, field2 AS field2_new, field3 AS field3_new
(For the latter two methods, I can break up the list into chunks of a few hundred rows to avoid going over the limit for long queries.)
So the question is, which is the fastest method to use? And is there another one that I don't know about?

MySQL: how to update column using value before change

There is a table with three column: id, field1, field2.
And there is a row: id=1, field1=1, field2=1.
Run a update SQL: UPDATE my_table SET field1=field2+1, field2=field1+1 WHERE id=1;
I expected the result is: id=1, field1=2, field2=2. But in fact I got: id=1, field1=2, field2=3. Because when calculating field2=field1+1, the value of field1 has changed!
I figure out a SQL to solve this problem:
UPDATE my_table dest, (SELECT * FROM my_table) src
SET dest.field1=src.field2+1, dest.field2=src.field1+1
WHERE dest.id=1;
However I want to insert a record, and if the row was existed then do a update just like above.
INSERT INTO my_table (id, field1, field2) VALUES(1, 1, 1)
ON DUPLICATE KEY UPDATE
field1=field2+1, field2=field1+1;
This SQL has problem same as the first SQL. So how can I do this update using the value before change with ON DUPLICATE KEY UPDATE clause?
Thanks for any help!
Couldn't think of anything else but a temp variable. However, couldn't think of a way to make SQL syntax work, other than this:
set #temp = 0;
update test.test set
f1 = (#temp:=f1),
f1 = f2 + 1,
f2 = #temp + 1
where id = 1;
Hope this helps, and hope even more it helps you find a better way :)
I find a trick way to do this.
Use the IF clause to create temp variable. Field update use temp variable to calculate.
INSERT INTO my_table (id, f1, f2) VALUES(1, 1, 1)
ON DUPLICATE KEY UPDATE
id=IF((#t1:=f1 & #t2:=f2), 1, 1), f1=#t2+1, f2=#t1+1;
There is some point to notice:
The performance is a bit slow. Especially copy TEXT value to temp variable.
If field id need to use IF clause, the expr will be more complicated like:
((#t1:=f1 & #t2:=f2) || TRUE) AND (Your Condition)

SQL how to use a formula to fill a column like in Excel

My database is in MySQL
I have a table, let's say of 4 columns.
I would like to know if it's possible, and how to implement the following: fill the 4th column according to the value of the column 2 and column 3
In Excel I have a formula, let's give an example: if column2 value is set to "grey" and column3 value is set to "car", then column 4 value should be set to "super"
I just say this as an example.
My real formula in Excel looks like this: =IF(K4=4;"Maximal";IF(K4>4;"Maximal";IF(K4=3;"Important";IF(K4>3;"Important";IF(K4=2;"Limited";IF(K4>2;"Limited";IF(K4=1;"Forgettable";IF(K4>1;"Forgettable";"error"))))))))
However I want to do it in SQL.
I was thinking of creating my table until the column 3, set column 4 to NULL or empty, then open a GUI written in Java and maybe there do a piece of code to automatically fill the column 4 according to what is in column 2 and column 3 (these values will be choosable via Choicelist).
But if there is a way to do it directly in SQL, I am interested
Thx a lot in advance for your help.
regards
Yes. you can easily update your NULL-values according to some requirements for the other values in other columns of a particular row with the Update statement
UPDATE <tablename>
SET <column> = 'value'
WHERE <condition>
The only drawback here might be that you have to create an update statement for each of the combinations of your values in column2 and column3. (however, it's not much work for your amount of conditions).
I created an example (demo):
Creating a table in SQL according to your example could look like this,I used a temporary one for the sake of an example:
CREATE GLOBAL TEMPORARY TABLE demoTable (
"Col1" VARCHAR2(50 BYTE) NOT NULL,
"Col2" VARCHAR2(50 BYTE) NOT NULL,
"Col3" VARCHAR2(50 BYTE) NOT NULL,
"Col4" VARCHAR2(50 BYTE) DEFAULT NULL
)
ON COMMIT PRESERVE ROWS
I also inserted some dummy data:
INSERT INTO demoTable VALUES ('Charles', 'grey', 'car', NULL);
INSERT INTO demoTable VALUES ('Alice', 'grey', 'bike', NULL);
INSERT INTO demoTable VALUES ('Bob', 'red', 'car', NULL);
The result:
Now, create the update statements like this, for example:
UPDATE demoTable dt
SET dt."Col4" = 'super'
WHERE dt."Col2" = 'grey' AND dt."Col3" = 'car';
The result
You can try like this;
select * from mytable
COL1 COL2
---- --------------------
0 -
1 -
2 -
3 -
4 -
4 record(s) selected.
update mytable Set Col2 =
Case
When Col1<1 Then 'error'
When Col1=1 Then 'Forget'
When Col1=2 Then 'Limited'
When Col1=3 Then 'Important'
When Col1=4 Then 'Maximal'
End"
select * from mytable"
COL1 COL2
---- --------------------
0 Error
1 Forget
2 Limited
3 Important
4 Maximal
4 record(s) selected.
You can create a sql function, lets say udfGetColumn4Value taking in the column2, column3 as parameters to it and return a value.
Now you can run a select column2, column3, udfGetColumn4Value(column2, column3) from table or a query as desired. Hope this helps.
You were not very precise regarding which DBMS you're using. And also about the exact logic behind using your two columns.
Still here comes a probable SQL-Server solution, where I have taken one statement using CASE WHEN with your example and concatenated your two columns col2 and col3 (you can apply your further logic of here) otherwise:
UPDATE TableName
SET Col4 = CASE WHEN col2 = 'red' AND col3 = 'car' THEN 'super' ELSE col2 + col3 END;
You should replace col2 + col3 with your further logic.
Seems that a simple UPDATE-Query could address your problem:
update things set result = "super" where thing = "car" and color = "grey";
The where-clause does what you desire to do by saying
fill the column 4 according to what is in column 2 and column 3
I created a test table here on turorialspoint, there you can check if it fits your needs.

MySQL CASE to update multiple columns

I would like to update multiple columns in my table using a case statement, but I cannot find how to do this (is this even possible). I came up with the following invalid reference query:
UPDATE tablename SET
CASE name
WHEN 'name1' THEN col1=5,col2=''
WHEN 'name2' THEN col1=3,col2='whatever'
ELSE col1=0,col2=''
END;
Is there any way of achieving the expected result with valid SQL?
UPDATE tablename
SET col1 = CASE WHEN name = 'name1' THEN 5
WHEN name = 'name2' THEN 3
ELSE 0
END
, col2 = CASE WHEN name = 'name1' THEN ''
WHEN name = 'name2' THEN 'whatever'
ELSE ''
END
;
I don't know of any clean way to do what you're asking. An equivalent valid SQL update would be:
UPDATE tablename SET
col1 = CASE name WHEN 'name1' THEN 5 WHEN 'name2' THEN 3 ELSE 0 END,
col2 = CASE name WHEN 'name1' THEN '' WHEN 'name2' THEN 'whatever' ELSE '' END;
Of course this isn't pretty and requires repeating the same cases (e.g. 'name1') multiple times, but I just don't think it's possible any other way.
If name has a unique index and your values are known to exist in the table, you can use this trick:
INSERT INTO tablename (name, col1, col2)
VALUES ('name1', 5, '')
, ('name2', 3, 'whatever')
ON DUPLICATE KEY UPDATE
col1 = VALUES(col1)
, col2 = VALUES(col2);
If there are any additional NOT NULL columns without a default value, you'll have to add dummy values for those. Just leave them out of the ON DUPLICATE KEY UPDATE and they'll be ignored.
You have two options that are covered in the different answers of this question:
UPDATE ... CASE
INSERT INTO ... ON DUPLICATE KEY
I had the following requirement:
bulk update 100000+ records with 4 column values
the source might have records that do not trigger the "ON DUPLICATE KEY" thus just inserting new data instead of just updating. At the same time I do not want these new records.
So there are two bad alternatives:
using UPDATE ... CASE for multiple columns makes this really expensive for the database when updating 100000+ records
using INSERT INTO ... ON DUPLICATE KEY might insert records that I do not want
My solution: Use INSERT INTO ... ON DUPLICATE KEY with dummy data, then delete all records that include column with the UNWANTED_INSERTED_DATA flag
I am inserting one column with UNWANTED_INSERTED_DATA as a flag where I can guarantee these values will never be part of any real data. The remaining columns are updated with the wanted data.
INSERT INTO tablename (name, col1, col2)
VALUES ('name1', 5, 'UNWANTED_INSERTED_DATA')
, ('name2', 3, 'UNWANTED_INSERTED_DATA')
ON DUPLICATE KEY UPDATE
name = VALUES(name)
, col1 = VALUES(col1)
; # DO NOT include the UNWANTED_INSERTED_DATA in the update part
The records I want only use update thus never receive the dummy data with UNWANTED_INSERTED_DATA column.
The records I do not want contain the dummy data.
I can then delete all unwanted inserted records with dummy data in one delete query:
DELETE FROM tablename WHERE col2 = 'UNWANTED_INSERTED_DATA'
This is a hacky solution but gets the job done with just two queries in two steps.

Is it really no solution to update multiple records in MySQL?

I want to do all these update in one statement.
update table set ts=ts_1 where id=1
update table set ts=ts_2 where id=2
...
update table set ts=ts_n where id=n
Is it?
Use this:
UPDATE `table` SET `ts`=CONCAT('ts_', `id`);
Yes you can but that would require a table (if only virtual/temporary), where you's store the id + ts value pairs, and then run an UPDATE with the FROM syntax.
Assuming tmpList is a table with an id and a ts_value column, filled with the pairs of id value, ts value you wish to apply.
UPDATE table, tmpList
SET table.ts = tmpList.ts_value
WHERE table.id = tmpList.id
-- AND table.id IN (1, 2, 3, .. n)
-- above "AND" is only needed if somehow you wish to limit it, i.e
-- if tmpTbl has more idsthan you wish to update
A possibly table-less (but similar) approach would involve a CASE statement, as in:
UPDATE table
SET ts = CASE id
WHEN 1 THEN 'ts_1'
WHEN 2 THEN 'ts_2'
-- ..
WHEN n THEN 'ts_n'
END
WHERE id in (1, 2, ... n) -- here this is necessary I believe
Well, without knowing what data, I'm not sure whether the answer is yes or no.
It certainly is possible to update multiple rows at once:
update table table1 set field1='value' where field2='bar'
This will update every row in table2 whose field2 value is 'bar'.
update table1 set field1='value' where field2 in (1, 2, 3, 4)
This will update every row in the table whose field2 value is 1, 2, 3 or 4.
update table1 set field1='value' where field2 > 5
This will update every row in the table whose field2 value is greater than 5.
update table1 set field1=concat('value', id)
This will update every row in the table, setting the field1 value to 'value' plus the value of that row's id field.
You could do it with a case statement, but it wouldn't be pretty:
UPDATE table
SET ts = CASE id WHEN 1 THEN ts_1 WHEN 2 THEN ts_2 ... WHEN n THEN ts_n END
I think that you should expand the context of the problem. Why do you want/need all the updates to be done in one statement? What benefit does that give you? Perhaps there's another way to get that benefit.
Presumably you are interacting with sql via some code, so certainly you can simply make sure that the three updates all happen atomically by creating a function that performs all three of the updates.
e.g. pseudocode:
function update_all_three(val){
// all the updates in one function
}
The difference between a single function update and some kind of update that performs multiple updates at once is probably not a very useful distinction.
generate the statements:
select concat('update table set ts = ts_', id, ' where id = ', id, '; ')
from table
or generate the case conditions, then connect it to your update statement:
select concat('when ', id, ' then ts_', id) from table
You can use INSERT ... ON DUPLICATE KEY UPDATE. See this quesion: Multiple Updates in MySQL
ts_1, ts_2, ts_3, etc. are different fields on the same table? There's no way to do that with a single statement.