在 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`;

不那么棘手,最后有两个值:

  1. LAST_INSERT_ID() as f是第一个插入的行ID
  2. MAX(id) as l 这是最后插入的行 ID

因此,有了这两个边界,您肯定拥有所有插入的行 ID。说它有缺点,那就是你总是有一个 LAST_INSERT_ID() 值,即使行只受到更新语句的影响。但是,当您用 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;