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;
I was trying with this code but it didn't work. it's always get the MAX(eq_no) as 0
$sql1 =mysqli_query($con, "SELECT MAX(eq_no) AS val FROM tech_add_equip");
$sql2 = "INSERT INTO time (eq_no,status_no) VALUES ('$val', 4 );";
if (!mysqli_query($con,$sql2)) {
die('Error: ' . mysqli_error($con)); };
Finally, after I try with this code, it inserts in the right number of MAX(eq_no) but i still cant insert the values of status_no
INSERT INTO time (eq_no) SELECT MAX(eq_no) AS vale FROM tech_add_equip
Could you suggest me what did i missing in the code?
Thank you for your helping
One row returned from SELECT a,b,c statement in sub query is equivalent to set of values that is otherwise hardcoded as ('a-value','b-value','c-value')*. You can hardcode a value within select as well:
INSERT INTO time (eq_no, status_no)
SELECT MAX(eq_no), 4
FROM tech_add_equip
No need for aliases within select - order of columns matters.
*) One row result can be used for IN() clause. Another row would become set of values after comma - can't be uset for IN(), but it works ok for INSERT
('row1-a-value', 'row1-b-value'), ('row2-a-value', 'row2-b-value')
$max = SELECT MAX( customer_id ) FROM customers;
INSERT INTO customers( customer_id, statusno )
VALUES ($max , 4)
I have a PDO statement that insert or update a row depending on a UNIQUE column (if the column exist then update) . I wonder if there is a solution for both to get the id of the INSERTED or UPDATED that I could use in the same query. So ...
considering this answer ... how could it be together?
SET #update_id := 0;
UPDATE some_table SET column_name = 'value', id = (SELECT #update_id := id)
WHERE some_other_column = 'blah' LIMIT 1;
SELECT #update_id;
plus
lastInsertId();
meaning something like (I'm not sure if there would be a sintax for this so I just set what i have in mind:
IF "INSERT" then SELECT lastInsertId()
else if "UPDATE" then select ID of the last row updated;
I have two tables first called messages and the other called messages_reply.
I used this code to insert into messages table:
$query = "INSERT INTO `messages` VALUES('', '$id', '$otherId', '')";
$query_run = mysqli_query($connect, $query);
I have the first column auto_increment thats why I left it empty by writing ''
Now i want this auto_increment value that i have inserted to be inserted in the other table called messages_reply
Do I have to create another query to return it or there is an instant way to insert it here and there?
you have to select the last id on table messages first, then you can insert that last id + 1 into messages reply
$query_sel_last_id = "SELECT id FROM messages ORDER BY id desc LIMIT 1"; // select the last id
after that, you only need to insert to messages_reply, remember to plus the value
$query_sel_last_id + 1
EDIT: gordon's solution is better and simpler, LAST_INSERT_ID()
i amn trying to run an insert query to insert the NOW() time and date only if certian enum values in my table are set to yes.
my table has 4 columns 'form1_completed', 'form2_completed', 'form3_completed', 'form4_completed'
the columns will be yes or no.
i am using this query to try and insert the current time and date into the database, its inserting but it doesnt pay attention to the where clause and just inserts anyway, can someone please show me what im doing wrong
$query2 = "IF (SELECT * FROM `supplier_session` WHERE `form1_completed` = 'Yes' AND `form2_completed` = 'Yes' AND `form3_completed` = 'Yes' AND `form4_completed` = 'Yes') INSERT INTO `supplier_session` (`completed_date`) VALUES (NOW());
WHERE `user_IP` = '$ipaddress '";
Instead of
WHERE `form1_completed` = Yes
use
WHERE `form1_completed` = 'Yes'
I think it should be:
IF EXISTS (SELECT * FROM ...) INSERT INTO ...
EXISTS (subquery) is true when the subquery returns any rows.
Or you could do:
IF (SELECT COUNT(*) FROM ...) INSERT INTO ...