web-dev-qa-db-ja.com

更新部分で新しい値と古い値の両方を使用できるように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' )

私の問題は、実際のテーブルに複数の列があることです。更新部分に複数の選択ステートメントを書くことは良い考えですか?

もちろん、コードでそれを行うことができますが、より良い方法はありますか?

60

私のコメントで述べたように、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

ほとんどのことは本当に単純であることに注意してください。単純でなければならないことを過度に複雑にしている場合は、おそらく間違った方法でやっている可能性が高いです:)

129
Michael J.V.

この例からアイデアを得ることができます:

ユーザーごとに7日間のデータを追加するとします。

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 |
+--------+-----+-------+-------+
11
Salim

マイケルの答えは正しいものですが、プログラムでアップサートを行うにはもう少し知っておく必要があります。

まず、テーブルを作成し、一意のインデックスが必要な列を指定します。

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 );

キー「id_ce」のエントリ「1-199」が重複しています

そのため、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

したがって、上記のコマンドを実行した後でも、テーブルには1行しかなく、値は302.0

6
Nav

PKカラムの値、またはユニシティを満たすカラムのユニークインデックスがある場合、INSERT IGNOREINSERT INTO ... ON DUPLICATE、またはREPLACEを使用できます。

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
    (...);

これはアップサートの構文です

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