如何执行UPSERT以便我可以在更新部分中使用新旧值

时间:2011-05-24 08:36:21

标签: mysql performance upsert

愚蠢而简单的例子: 假设我有一个表'Item',其中我保留了收到的项目的总数。

Item_Name              Items_In_Stock

项目名称是主键。当我收到数量为X的物品A时,如何实现以下目的。

如果该项目不存在,我为项目A插入一个新的,并将库存中的项目设置为X,如果存在库存中的项目为Y的记录,那么库存中的项目中的新值为(X + Y)

INSERT INTO `item`
(`item_name`, items_in_stock)
VALUES( 'A', 27)
ON DUPLICATE KEY UPDATE
`new_items_count` = 27 + (SELECT items_in_stock where item_name = 'A' )

我的问题是我的实际表中有多列。在更新部分中编写多个select语句是个好主意吗?

当然我可以在代码中完成,但是有更好的方法吗?

7 个答案:

答案 0 :(得分:123)

正如我的评论中所提到的,您不必执行子选择来引用导致ON DUPLICATE KEY触发的行。因此,在您的示例中,您可以使用以下内容:

INSERT INTO `item`
(`item_name`, items_in_stock)
VALUES( 'A', 27)
ON DUPLICATE KEY UPDATE
`new_items_count` = `new_items_count` + 27

请记住,大多数事情都非常简单,如果你发现自己过于复杂的事情应该很简单,那么你很可能以错误的方式做事:)

答案 1 :(得分:11)

您可以从这个示例中了解到:

假设您要添加用户明智的七天数据

它应该具有userid和day的唯一值,如

UNIQUE KEY `seven_day` (`userid`,`day`)

这是表格

CREATE TABLE `table_name` (
  `userid` char(4) NOT NULL,
  `day` char(3) NOT NULL,
  `open` char(5) NOT NULL,
  `close` char(5) NOT NULL,
  UNIQUE KEY `seven_day` (`userid`,`day`)
);

您的查询将是

INSERT INTO table_name (userid,day,open,close) 
    VALUES ('val1', 'val2','val3','val4') 
        ON DUPLICATE KEY UPDATE open='val3', close='val4';

示例:

<?php
//If your data is
$data= array(
        'sat'=>array("userid"=>"1001", "open"=>"01.01", "close"=>"11.01"),
        'sun'=>array("userid"=>"1001", "open"=>"02.01", "close"=>"22.01"),
        'sat'=>array("userid"=>"1001", "open"=>"03.01", "close"=>"33.01"),
        'mon'=>array("userid"=>"1002", "open"=>"08.01", "close"=>"08.01"),
        'mon'=>array("userid"=>"1002", "open"=>"07.01", "close"=>"07.01")
    );


//If you query this in a loop
//$conn = mysql_connect("localhost","root","");
//mysql_select_db("test", $conn);

foreach($data as $day=>$info) {
    $sql = "INSERT INTO table_name (userid,day,open,close) 
                VALUES ('$info[userid]', '$day','$info[open]','$info[close]') 
            ON DUPLICATE KEY UPDATE open='$info[open]', close='$info[close]'";
    mysql_query($sql);
}
?>

您的数据将在表格中:

+--------+-----+-------+-------+
| userid | day | open  | close |
+--------+-----+-------+-------+
| 1001   | sat | 03.01 | 33.01 |
| 1001   | sun | 02.01 | 22.01 |
| 1002   | mon | 07.01 | 07.01 |
+--------+-----+-------+-------+

答案 2 :(得分:5)

尽管Michael的答案是正确的,但您需要了解更多以编程方式进行upsert:

首先,创建表并指定要在其上创建唯一索引的列:

CREATE TABLE IF NOT EXISTS Cell (
  cellId BIGINT UNSIGNED,
  attributeId BIGINT UNSIGNED,
  entityRowId BIGINT UNSIGNED,
  value DECIMAL(25,5),
  UNIQUE KEY `id_ce` (`cellId`,`entityRowId`)
)

然后在其中插入一些值:

INSERT INTO Cell VALUES( 1, 6, 199, 1.0 );

尝试再次执行相同操作,并且您会收到重复的密钥错误,因为cellIdentityRowId相同:

INSERT INTO Cell VALUES( 1, 6, 199, 1.0 );
  

重复录入&#39; 1-199&#39;关键字&#39; id_ce&#39;

这就是我们使用upsert命令的原因:

INSERT INTO Cell ( cellId, attributeId, entityRowId, value, s, l, p, t )
VALUES( 1, 6, 199, 300.0 )
ON DUPLICATE KEY UPDATE `value` = `value` + VALUES(`value`)

此命令将已经存在的值1.0作为值并执行value = value + 300.0

因此,即使执行上述命令后,表中也只有一行,其值为302.0

答案 3 :(得分:0)

这是upsert的语法

INSERT INTO `{TABLE}` (`{PKCOLUMN}`, `{COLUMN}`) VALUES (:value)
ON DUPLICATE KEY UPDATE `{COLUMN}` = :value_dup';

答案 4 :(得分:0)

如果您具有“ PK列”或满足唯一性的列上的“唯一索引”的值,则可以使用INSERT IGNOREINSERT INTO ... ON DUPLICATEREPLACE

带有INSERT IGNORE

的示例
INSERT IGNORE INTO Table1
    (ID, serverID, channelID, channelROLE)
VALUES
    (....);

带有INSERT INTO .. ON DUPLICATE KEY UPDATE

的示例
SET @id = 1,
    @serverId = 123545,
    @channelId = 512580,
    @channelRole = 'john';
INSERT INTO Table1
    (ID, serverID, channelID, channelROLE)
VALUES
    (@id, @serverId, @channelId, @channelRole)
ON DUPLICATE KEY UPDATE
    serverId = @serverId,
    channelId = @channelId,
    channelRole = @channelRole;

带有Replace

的示例
REPLACE INTO table1
    (ID, serverID, channelID, channelROLE)
VALUES
    (...);

答案 5 :(得分:0)

Upsert示例

INSERT INTO table1 (col1, col2, col3)
VALUES ($1, $2, $3)
ON CONFLICT (col1)
DO
UPDATE
SET col2 = $2, col3 = $3
WHERE col1 = $1
RETURNING col1

答案 6 :(得分:0)

迟到的答案,但这可以帮助仍在搜索相同内容的人。

我们可以通过REPLACE查询

来实现

首先,我们需要将 Item_Name 字段设置为 UNIQUE 以执行更新。

REPLACE INTO item (item_name, items_in_stock) VALUES('A', 27);

这将使用 Item_Name = 'A' 更新数据。

该行将先被删除,然后作为新插入。