在 insert_update 查询中获取 insert_id 和更新的行 ID
Get both insert_id and updated row id in insert_update query
我有这样的查询:
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;
请参阅 here 了解获取更新行 ID 的方法。
如果它更新任何行,我会在 @uids
中获取更新的行 ID',但如果插入了一行,我无法获取该行的 ID。那么如何同时获取插入行id和更新行id呢?
或者如何在 ON DUPLICATE KEY...
之前的插入中执行 (SELECT @uids := concat_ws(',', LAST_INSERT_ID(), @uids))
?
时间很短,我们很长
你不能这样做,因为在插入时无法填充 @uids
需要 select
子句,并且不允许在其中使用 select
子句insert
语句,除非您的查询可以转换为 INSERT ... SELECT
.
长答案
只要您不尝试插入可能导致更新和插入的混合值(您可能会这样做),您可以使用一种讨厌但安全的方法搭配:
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`;
不那么棘手,最后有两个值:
LAST_INSERT_ID() as f
是第一个插入的行ID
MAX(id) as l
这是最后插入的行 ID
因此,有了这两个边界,您肯定拥有所有插入的行 ID。说它有缺点,那就是你总是有一个 LAST_INSERT_ID()
值,即使行只受到更新语句的影响。但是,当您用 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.
你可以自己试试看行不行。如果您得到一个预期的 return 值,那么您可以了解您的查询是否进行了一些更新或插入,并根据该
读取结果
作为我的简短回答,没有正确的方法可以在相同的查询上下文中执行此操作,但以编程方式执行此操作可能更简洁? (虽然我不打赌它的表现)
$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);
示例输出:
array(1) {
[0]=>
int(140)
}
array(1) {
[0]=>
int(337)
}
另一种解决方法是使用 MySQL triggers。通过在 table tbl1
上创建 AFTER INSERT
触发器,您可以存储 ID 供以后使用:
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;
我有这样的查询:
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;
请参阅 here 了解获取更新行 ID 的方法。
如果它更新任何行,我会在 @uids
中获取更新的行 ID',但如果插入了一行,我无法获取该行的 ID。那么如何同时获取插入行id和更新行id呢?
或者如何在 ON DUPLICATE KEY...
之前的插入中执行 (SELECT @uids := concat_ws(',', LAST_INSERT_ID(), @uids))
?
时间很短,我们很长
你不能这样做,因为在插入时无法填充 @uids
需要 select
子句,并且不允许在其中使用 select
子句insert
语句,除非您的查询可以转换为 INSERT ... SELECT
.
长答案
只要您不尝试插入可能导致更新和插入的混合值(您可能会这样做),您可以使用一种讨厌但安全的方法搭配:
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`;
不那么棘手,最后有两个值:
LAST_INSERT_ID() as f
是第一个插入的行IDMAX(id) as l
这是最后插入的行 ID
因此,有了这两个边界,您肯定拥有所有插入的行 ID。说它有缺点,那就是你总是有一个 LAST_INSERT_ID()
值,即使行只受到更新语句的影响。但是,当您用 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.
你可以自己试试看行不行。如果您得到一个预期的 return 值,那么您可以了解您的查询是否进行了一些更新或插入,并根据该
读取结果作为我的简短回答,没有正确的方法可以在相同的查询上下文中执行此操作,但以编程方式执行此操作可能更简洁? (虽然我不打赌它的表现)
$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);
示例输出:
array(1) {
[0]=>
int(140)
}
array(1) {
[0]=>
int(337)
}
另一种解决方法是使用 MySQL triggers。通过在 table tbl1
上创建 AFTER INSERT
触发器,您可以存储 ID 供以后使用:
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;