在insert_update查询中获取insert_id和更新的行id

时间:2016-06-29 10:04:17

标签: php mysql insert-update

我有这样的查询:

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的方法。

我得到了更新的行ID'在@uids中,如果它更新了任何行,但是如果插入了一行,我就无法获得该行的ID。那么如何获得插入的行ID和更新的行ID?

如何在(SELECT @uids := concat_ws(',', LAST_INSERT_ID(), @uids))之前的插入中执行ON DUPLICATE KEY...

1 个答案:

答案 0 :(得分:1)

时间短,我们很长

您无法执行此操作,因为在插入需要@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. 最后插入行ID的
  3. MAX(id) as l
  4. 因此,通过这两个边界,您可以完全插入行ID。说它有缺点,即使行只受更新语句的影响,你总是有一个LAST_INSERT_ID()值。但是,当您使用标记问题时,有mysqli_affected_rows时有机会从multi_query获益,但我无法从mysqli_affected_rows生成预期的返回值{是documented by MySQL

      

    对于INSERT ... ON DUPLICATE KEY UPDATE语句,受影响的行   如果将行作为新行插入,则每行的值为1;如果是,则为2   更新现有行,如果现有行设置为更新,则为0   当前价值。

    您可以自己尝试一下,看看它是否有效。如果您获得了预期的返回值,那么您可以了解您的查询是否已完成某些更新或插入并根据该查询读取结果

    作为我的简短回答,没有正确的方法在同一个查询上下文中执行它但可能以编程方式执行它更整洁? (虽然我不打赌它的表现

    $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。通过在表AFTER INSERT上创建tbl1触发器,您可以存储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;