How to bulk update mysql data with one query? - php

$query = mysql_query("UPDATE a SET fruit = '**apple**' WHERE id = '**1**' ");
$query2 = mysql_query("UPDATE a SET fruit = '**orange**' WHERE id = '**2**' ");
$query3 = mysql_query("UPDATE a SET fruit = '**peach**' WHERE id = '**3**' ");
is there any way to simplify it to one query?

I found a following solution:
INSERT into `table` (id,fruit)
VALUES (1,'apple'), (2,'orange'), (3,'peach')
ON DUPLICATE KEY UPDATE fruit = VALUES(fruit);
Id must be unique or primary key.
But don't know about performance.

Yes you can do it using this query:
UPDATE a
SET fruit = (CASE id WHEN 1 THEN 'apple'
WHEN 2 THEN 'orange'
WHEN 3 THEN 'peach'
END)
WHERE id IN(1,2 ,3);

Using IF() function in MySQL this can be achieved as
UPDATE a
SET fruit = IF (id = 1, 'apple', IF (id = 2, 'orange', IF (id = 3, 'peach', fruit)));

Based on the warning message
'VALUES function' is deprecated and will be removed in a future release. Please use an alias (INSERT INTO ... VALUES (...) AS alias) and replace VALUES(col) in the ON DUPLICATE KEY UPDATE clause with alias.col instead
One may consider a slight modification of Yaroslav's solution like so:
INSERT into `table` (id,fruit)
VALUES (1,'apple'), (2,'orange'), (3,'peach') as tb
ON DUPLICATE KEY UPDATE fruit = tb.fruit;
It does just the same thing but mutes the warning message.

Related

Get both insert_id and updated row id in insert_update query

I have a query like this:
SET #uids = '';
INSERT INTO tbl1 (name,used,is_active)
VALUES (1,0,0),(2,0,0),(24,0,0)
ON DUPLICATE KEY UPDATE
id = LAST_INSERT_ID(id)
, used = (SELECT #uids := concat_ws(',', LAST_INSERT_ID(), #uids))
, used = used+1
, is_active = CASE WHEN used > 3 THEN 1 ELSE 0 END;
SELECT #uids;
See here to figure out the way of getting updated row id.
I get updated row ids' in #uids if it updates any rows but if a row is inserted, I can't get the id of that. So how to get both inserted row id and updated row id?
Or how to execute (SELECT #uids := concat_ws(',', LAST_INSERT_ID(), #uids)) in insert before ON DUPLICATE KEY... ?
Time's short and we are long
You can't do it, because there is no way to fill #uids while inserting which needs a select clause and you are not allowed to use a select clause within an insert statement unless your query can be transformed into an INSERT ... SELECT.
Long answer
As long as you don't try to insert mixed values that may result in both updating and inserting (which probably you do) there is a nasty but safe way you can go with:
SET #uids := '';
INSERT INTO `tbl1` (name, used, is_active)
VALUES (1,0,0),(2,0,0),(24,0,0)
ON DUPLICATE KEY UPDATE
is_active = CASE WHEN used > 3 THEN 1 ELSE 0 END,
id = LAST_INSERT_ID(id),
used = used + 1,
id = (SELECT #uids := concat_ws(',', LAST_INSERT_ID(), #uids));
SELECT #uids, LAST_INSERT_ID() as f, MAX(id) as l from `tbl1`;
Being not so tricky, you have two values at the end:
LAST_INSERT_ID() as f is the first inserted row ID
MAX(id) as l which is last inserted row ID
So with that two boundaries you surly have all inserted rows IDs. Saying that it has drawbacks and that is you always have a LAST_INSERT_ID() value even if rows only were affected by update statement. However as you tagged your question with php there was a chance to get benefit from mysqli_affected_rows while doing a multi_query but I couldn't produce expected return values from mysqli_affected_rows as is documented by MySQL:
For INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows
value per row is 1 if the row is inserted as a new row, 2 if an
existing row is updated, and 0 if an existing row is set to its
current values.
You can try it yourself and see if it works. If you get an expected return value then you can understand if your query has done some updates or inserts and read results based on that
As my short answer, there is no correct way to do it within the same query context but may be doing it programatically is neater? (though I don't bet on its performance)
$values = [[1, 0, 0], [2, 0, 0], [24, 0, 0]];
$insertIDs = [];
$updateIDs = [];
foreach ($values as $v) {
$insert = $mysqli->prepare("INSERT INTO `tbl1` (name, used, is_active) VALUES (?, ?, ?)");
$insert->bind_param('ddd', $v[0], $v[1], $v[2]);
$insert->execute();
if ($insert->affected_rows == -1) {
$update = $mysqli->prepare("UPDATE `tbl1` SET id = LAST_INSERT_ID(id), used = used + 1, is_active = CASE WHEN used > 3 THEN 1 ELSE 0 END WHERE name = ?"); // considering `name` as a unique column
$update->bind_param('d', $v[0]);
$update->execute();
if ($update->affected_rows == 1) {
$updateIDs[] = $update->insert_id;
}
} else {
$insertIDs[] = $insert->insert_id;
}
}
var_dump($updateIDs);
var_dump($insertIDs);
Example output:
array(1) {
[0]=>
int(140)
}
array(1) {
[0]=>
int(337)
}
One another workaround could be using MySQL triggers. By creating an AFTER INSERT trigger on table tbl1, you are able to store IDs for later use:
CREATE TRIGGER trigger_tbl1
AFTER INSERT
ON `tbl1` FOR EACH ROW
BEGIN
UPDATE `some_table` SET last_insert_ids = concat_ws(',', LAST_INSERT_ID(), last_insert_ids) WHERE id = 1;
END;

(PDO PHP) The fastest way to update or insert multiple rows?

I don't know how to update or insert multiple rows by using PDO. Please help me.
Something that is in my mind is:
$stmt = $dbh->query("update_line_1; update_line_2; update_line_3");
//update_line_1: update table a set a.column1 = "s1" where a.id = 1
//update_line_2: update table a set a.column1 = "s2" where a.id = 2
//....
$stm = $dbh->query("insert_line_1; insert_line_3; insert_line_3");
//something is like the update line above.
I don't know this way works or not. And If you have another way, please let me know. Thank you so so much.
And if I use prepare statement, I just update each row each time. (This is much more safe than above)
$stmt = $dbh->prepare("update table a set a.colum1 = :column1 where a.id = :id");
$stmt->bindParam(":column1","s1");
$stmt->bindparam(":id",1);
$stmt->execute();
The most hate thing I don't want to do is using a loop goes through all elements in an array, and update or insert each element each time
Is another way to mass safely update or insert multiple rows to database? thank for your help.
Sorry about my English.
For inserts, you can insert multiple rows worth of data with the following syntax:
INSERT INTO table (col1, col2, col3)
VALUES
('1', '2', '3'),
('a', 'b', 'c'),
('foo', 'bar', 'baz')
For updates, the update will by default effect as many rows as meet the criteria of the query. So something like this would update an entire table
UPDATE table SET col = 'a'
If you are trying to update different values for each row, you don't really have much of a choice other than to do a query for each operation. I would suggest however that, building on your PDO example, you could do something like this:
$update_array = array(
1 => 'foo',
2 => 'bar',
10 => 'baz'
); // key is row id, value is value to be updated
$stmt = $dbh->prepare("UPDATE table SET column1 = :column1 where id = :id");
$stmt->bindParam(":column1",$column_value);
$stmt->bindparam(":id",$id);
foreach($update_array as $k => $v) {
$id = $k
$column_value = $v;
$stmt->execute();
// add error handling here
}
With this approach you are at least leveraging the use of the prepared statement to minimize query overhead.

PHP Mysql How to make a field get the same value of the auto increment id [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
MySQL trigger to update a field to the value of id
Please I have a table with three fields :
Id (auto increment)
GroupById
Text
And I want when inserting a new row : If I left the field groupById blank, it must get by default the same value of the field Id.
Please have you any Idea ? Thanks in advance.
Edit : My code :
mysql_query("INSERT INTO group SET GroupById = (must get the current Id), Text = 'bla bla bla' ");
How about a trigger:
DELIMITER $$
CREATE TRIGGER trg_yourtable
BEFORE INSERT ON yourtable
FOR EACH ROW
BEGIN
IF NEW.GroupById IS NULL THEN
SET NEW.GroupById = (
SELECT AUTO_INCREMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yourtable'
);
END IF;
END $$
I'm not sure how safe this is... or what happens when you insert multiple rows from one query, or when two connections attempt to insert at the same time... but it works.
This simple SQL should do what you want:
INSERT INTO myTable (GroupById, Text) VALUES (NULL, 'your text');
SET #lastID = LAST_INSERT_ID();
UPDATE myTable SET GroupById = #lastID WHERE Id = #lastID;
Use a stored procedure
Get the MAX id
Add 1 to it and add insert it into both rows
You get the desired result in a single transaction.
Hope this helps.
Use this function | Tested
Example of use:
function get_current_insert_id($table)
{
$q = "SELECT LAST_INSERT_ID() FROM $table";
return mysql_num_rows(mysql_query($q)) + 1;
}
$txt = "text";
$groupID = '';
if ( empty($groupID) ) { $groupID = get_current_insert_id(test); }
$query = mysql_query("INSERT INTO test VALUES ('', '$groupID', '$txt') ");
if ( $query ) { echo 'Saved using ID:' . $groupID; } else { echo 'Oh noes!' . $query; }
Looks like you might need to run 2 queries:
insert into 'table' ('GroupById', 'Text') VALUES ('{group id}', '{sometext}')
update 'table' set GroupById = id where GroupById = null - it mightbe 0 instead of null depending on what you insert in db.
You can always optimize it through indexes, limits, order.

bulk updating a list of values from a list of ids

I'm frequently facing this issue, as an Oracle user, playing around with MySql.
Be the following situation:
a list of ids (1, 2, 3, ..., n)
a list of values ('val1', 'val2', 'val3', ..., 'valn') [The values are obviously totally different than these]
The 2 previous lists are passed ordered. It means the value passed first corresponds to the id passed first.
The objective is to update all the value of the table value having the corresponding id: val1 should update id 1, val2 should update id 2 etc... In only ONE query.
The easy solution is to update n times:
UPDATE `value` SET `value`='val1' WHERE id = 1;
UPDATE `value` SET `value`='val2' WHERE id = 2;
UPDATE `value` SET `value`='val3' WHERE id = 3;
...
UPDATE `value` SET `value`='valn' WHERE id = n;
But I would love to bulk update all this.
sqlfiddle to play a bit: http://sqlfiddle.com/#!2/d02e8
Here is one way to do this using one query. It won't be the prettiest-formatted query, but it will be just one.
<?php
$id_list = implode(',', $ids);
$whens = implode(
"\n ",
array_map(
function ($id, $value) {
return "WHEN {$id} THEN {$value}";
},
$ids,
$values
)
);
$sql = "
UPDATE value
SET value = CASE id
{$whens}
END
WHERE id IN ({$id_list})
";
?>
See my modified SQLFiddle.

Update MySQL row with "values" statement

The thing is that i have some dynamic columns and i use always the same php ajax page. So i get from the other page some coma separated strings. One with the columns, other with the table and other with the data. In the insert there is no problem because i do it this way:
$query = 'INSERT INTO '.$_GET["table"].' ('.$_GET["columns"].') VALUES('.$_GET["data"].')';
mysql_query($query, $link);
That transforms to this:
INSERT sys_users_cfg (usr,pwd,permission_id,image) VALUES ('pepwe2','1234','1','')
I need to do the UPDATE in the same way. with VALUES statement. Like:
UPDATE sys_users_cfg (usr,pwd,permission_id,image) VALUES ('pepwe2','1234','1','') WHERE usr_id = 33
That dosnt work. Is this posible?
Use the REPLACE INTO syntax instead if you're updating using the primary key, e.g.
REPLACE INTO sys_users_cfg (usr_id,usr,pwd,permission_id,image) VALUES (33,'pepwe2','1234','1','').
MySQL update is in a different format, see http://www.tizag.com/mysqlTutorial/mysqlupdate.php
UPDATE table SET column = 'value', column2 = 'value2' WHERE id = ID
This is how you update a row :
UPDATE sys_users_cfg SET usr = 'pepwe2', pwd = '1234', permission_id = 1, image = '' WHERE usr_id = 33
You can go read http://dev.mysql.com/doc/refman/5.0/en/update.html for more information.
$values = explode(',', $_GET['data']);
$query = "UPDATE sys_users_cfg SET usr = '$values[0]', pwd = '$values[1]', permission_id = '$values[2]', image = '' WHERE usr_id = '33'";

Categories