431

一度に複数の行を挿入できることは知っていますが、MySQL で複数の行を一度に (1 つのクエリのように) 更新する方法はありますか?

編集:たとえば、次のものがあります

Name   id  Col1  Col2
Row1   1    6     1
Row2   2    2     3
Row3   3    9     5
Row4   4    16    8

次のすべての更新を 1 つのクエリに結合したい

UPDATE table SET Col1 = 1 WHERE id = 1;
UPDATE table SET Col1 = 2 WHERE id = 2;
UPDATE table SET Col2 = 3 WHERE id = 3;
UPDATE table SET Col1 = 10 WHERE id = 4;
UPDATE table SET Col2 = 12 WHERE id = 4;
4

18 に答える 18

703

はい、可能です - INSERT ... ON DUPLICATE KEY UPDATE を使用できます。

あなたの例を使用して:

INSERT INTO table (id,Col1,Col2) VALUES (1,1,1),(2,2,3),(3,9,3),(4,10,12)
ON DUPLICATE KEY UPDATE Col1=VALUES(Col1),Col2=VALUES(Col2);
于 2008-08-06T14:33:41.560 に答える
139

動的な値があるため、列を更新するには IF または CASE を使用する必要があります。ちょっと見苦しくなりますが、うまくいくはずです。

あなたの例を使用すると、次のようにすることができます。

UPDATE テーブル SET Col1 = CASE id
                          いつ 1 そのとき 1
                          とき 2 とき 2
                          いつ 4 から 10
                          ELSE Col1
                        終わり、
                 Col2 = ケース ID
                          とき 3 とき 3
                          とき 4 とき 12
                          ELSE Col2
                        終わり
             WHERE id IN (1, 2, 3, 4);
于 2008-09-17T14:55:02.717 に答える
82

別の便利なオプションがまだ言及されていない理由がわからない:

UPDATE my_table m
JOIN (
    SELECT 1 as id, 10 as _col1, 20 as _col2
    UNION ALL
    SELECT 2, 5, 10
    UNION ALL
    SELECT 3, 15, 30
) vals ON m.id = vals.id
SET col1 = _col1, col2 = _col2;
于 2013-09-26T16:01:40.237 に答える
50

以下はすべて InnoDB に適用されます。

3つの異なる方法の速度を知ることは重要だと感じています.

3 つの方法があります。

  1. INSERT: ON DUPLICATE KEY UPDATE を使用した INSERT
  2. TRANSACTION: トランザクション内の各レコードの更新を行う場所
  3. CASE: UPDATE 内の異なるレコードごとにケース/いつ

これをテストしたところ、INSERT メソッドはTRANSACTION メソッドよりも 6.7 倍高速でした3,000 行と 30,000 行の両方のセットを試しました。

TRANSACTION メソッドは、実行中に結果をメモリなどにバッチ処理しますが、それぞれのクエリを個別に実行する必要があります。これには時間がかかります。TRANSACTION メソッドも、レプリケーションとクエリ ログの両方でかなりコストがかかります。

さらに悪いことに、CASE メソッドは、 30,000 レコードの INSERT メソッドよりも41.1倍遅くなりました (TRANSACTION よりも 6.1 倍遅い)。MyISAMでは75倍遅くなります。INSERT メソッドと CASE メソッドは、レコード数が 1,000 まででも壊れました。100 レコードでも、CASE メソッドはかろうじて高速です。

したがって、一般的には、INSERT メソッドが最適で使いやすいと思います。クエリは小さくて読みやすく、アクションのクエリは 1 つだけです。これは InnoDB と MyISAM の両方に当てはまります。

ボーナス要素:

INSERT 非デフォルト フィールドの問題の解決策は、関連する SQL モードを一時的にオフにすることですSET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES","")sql_mode元に戻す予定がある場合は、必ず最初のものを保存してください。

auto_increment が INSERT メソッドを使用して上昇するという他のコメントについては、これは InnoDB では当てはまりますが、MyISAM では当てはまらないようです。

テストを実行するコードは次のとおりです。また、.SQL ファイルを出力して、php インタープリターのオーバーヘッドを取り除きます。

<?php
//Variables
$NumRows=30000;

//These 2 functions need to be filled in
function InitSQL()
{

}
function RunSQLQuery($Q)
{

}

//Run the 3 tests
InitSQL();
for($i=0;$i<3;$i++)
    RunTest($i, $NumRows);

function RunTest($TestNum, $NumRows)
{
    $TheQueries=Array();
    $DoQuery=function($Query) use (&$TheQueries)
    {
        RunSQLQuery($Query);
        $TheQueries[]=$Query;
    };

    $TableName='Test';
    $DoQuery('DROP TABLE IF EXISTS '.$TableName);
    $DoQuery('CREATE TABLE '.$TableName.' (i1 int NOT NULL AUTO_INCREMENT, i2 int NOT NULL, primary key (i1)) ENGINE=InnoDB');
    $DoQuery('INSERT INTO '.$TableName.' (i2) VALUES ('.implode('), (', range(2, $NumRows+1)).')');

    if($TestNum==0)
    {
        $TestName='Transaction';
        $Start=microtime(true);
        $DoQuery('START TRANSACTION');
        for($i=1;$i<=$NumRows;$i++)
            $DoQuery('UPDATE '.$TableName.' SET i2='.(($i+5)*1000).' WHERE i1='.$i);
        $DoQuery('COMMIT');
    }
    
    if($TestNum==1)
    {
        $TestName='Insert';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf("(%d,%d)", $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery('INSERT INTO '.$TableName.' VALUES '.implode(', ', $Query).' ON DUPLICATE KEY UPDATE i2=VALUES(i2)');
    }
    
    if($TestNum==2)
    {
        $TestName='Case';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf('WHEN %d THEN %d', $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery("UPDATE $TableName SET i2=CASE i1\n".implode("\n", $Query)."\nEND\nWHERE i1 IN (".implode(',', range(1, $NumRows)).')');
    }
    
    print "$TestName: ".(microtime(true)-$Start)."<br>\n";

    file_put_contents("./$TestName.sql", implode(";\n", $TheQueries).';');
}
于 2016-10-03T11:58:05.807 に答える
10
UPDATE table1, table2 SET table1.col1='value', table2.col1='value' WHERE table1.col3='567' AND table2.col6='567'

これでうまくいくはずです。

複数のテーブルについては、MySQL マニュアルに参照があります。

于 2008-08-06T14:14:33.737 に答える
9

一時テーブルを使用する

// Reorder items
function update_items_tempdb(&$items)
{
    shuffle($items);
    $table_name = uniqid('tmp_test_');
    $sql = "CREATE TEMPORARY TABLE `$table_name` ("
        ."  `id` int(10) unsigned NOT NULL AUTO_INCREMENT"
        .", `position` int(10) unsigned NOT NULL"
        .", PRIMARY KEY (`id`)"
        .") ENGINE = MEMORY";
    query($sql);
    $i = 0;
    $sql = '';
    foreach ($items as &$item)
    {
        $item->position = $i++;
        $sql .= ($sql ? ', ' : '')."({$item->id}, {$item->position})";
    }
    if ($sql)
    {
        query("INSERT INTO `$table_name` (id, position) VALUES $sql");
        $sql = "UPDATE `test`, `$table_name` SET `test`.position = `$table_name`.position"
            ." WHERE `$table_name`.id = `test`.id";
        query($sql);
    }
    query("DROP TABLE `$table_name`");
}
于 2011-04-07T07:34:08.543 に答える
3

同じテーブルに別名を付けて、挿入する ID を指定できます (行ごとの更新を行う場合:

UPDATE table1 tab1, table1 tab2 -- alias references the same table
SET 
col1 = 1
,col2 = 2
. . . 
WHERE 
tab1.id = tab2.id;

さらに、他のテーブルからも更新できることは明らかです。この場合、更新は "SELECT" ステートメントとしても機能し、指定しているテーブルからデータを取得します。クエリで更新値を明示的に指定しているため、2 番目のテーブルは影響を受けません。

于 2013-01-02T19:48:31.147 に答える
3

(複数の) インジェクション コマンドを防止するために実装された MySQL の「安全メカニズム」を無効にする、「マルチ ステートメント」と呼ばれる変更可能な設定があります。MySQL の「優れた」実装によくあることですが、ユーザーが効率的なクエリを実行することも妨げます。

ここ ( http://dev.mysql.com/doc/refman/5.1/en/mysql-set-server-option.html ) は、設定の C 実装に関する情報です。

PHP を使用している場合は、mysqli を使用して複数のステートメントを実行できます (php はしばらく前から mysqli に同梱されていると思います)。

$con = new mysqli('localhost','user1','password','my_database');
$query = "Update MyTable SET col1='some value' WHERE id=1 LIMIT 1;";
$query .= "UPDATE MyTable SET col1='other value' WHERE id=2 LIMIT 1;";
//etc
$con->multi_query($query);
$con->close();

それが役立つことを願っています。

于 2011-03-06T21:32:06.220 に答える
2

更新時に結合を使用することにも興味があるかもしれませんが、これも可能です。

Update someTable Set someValue = 4 From someTable s Inner Join anotherTable a on s.id = a.id Where a.id = 4
-- Only updates someValue in someTable who has a foreign key on anotherTable with a value of 4.

編集: 更新する値がデータベース内の別の場所から取得されていない場合は、複数の更新クエリを発行する必要があります。

于 2008-08-06T14:20:23.257 に答える
0

はい..INSERT ON DUPLICATE KEY UPDATE SQLステートメントを使用して可能です..構文:INSERT INTO table_name (a、b、c) VALUES (1,2,3)、(4,5,6) ON DUPLICATE KEY UPDATE a =値(a)、b=値(b)、c=値(c)

于 2014-08-09T10:16:59.350 に答える
0

使用する

REPLACE INTO`table` VALUES (`id`,`col1`,`col2`) VALUES
(1,6,1),(2,2,3),(3,9,5),(4,16,8);

ご注意ください:

  • id は一意の主キーである必要があります
  • 外部キーを使用してテーブルを参照する場合、REPLACE は削除してから挿入するため、エラーが発生する可能性があります。
于 2016-03-15T16:59:23.193 に答える
-2

以下は、1 つのテーブルのすべての行を更新します。

Update Table Set
Column1 = 'New Value'

次の行は、Column2 の値が 5 を超えるすべての行を更新します。

Update Table Set
Column1 = 'New Value'
Where
Column2 > 5

複数のテーブルを更新するすべてのUnkwntechの例があります

UPDATE table1, table2 SET
table1.col1 = 'value',
table2.col1 = 'value'
WHERE
table1.col3 = '567'
AND table2.col6='567'
于 2008-08-06T14:18:21.217 に答える
-4
UPDATE tableName SET col1='000' WHERE id='3' OR id='5'

これはあなたが探しているものを達成するはずです。IDを追加するだけです。私はそれをテストしました。

于 2008-08-06T14:22:37.837 に答える
-6
UPDATE `your_table` SET 

`something` = IF(`id`="1","new_value1",`something`), `smth2` = IF(`id`="1", "nv1",`smth2`),
`something` = IF(`id`="2","new_value2",`something`), `smth2` = IF(`id`="2", "nv2",`smth2`),
`something` = IF(`id`="4","new_value3",`something`), `smth2` = IF(`id`="4", "nv3",`smth2`),
`something` = IF(`id`="6","new_value4",`something`), `smth2` = IF(`id`="6", "nv4",`smth2`),
`something` = IF(`id`="3","new_value5",`something`), `smth2` = IF(`id`="3", "nv5",`smth2`),
`something` = IF(`id`="5","new_value6",`something`), `smth2` = IF(`id`="5", "nv6",`smth2`) 

// php のようにビルドするだけです

$q = 'UPDATE `your_table` SET ';

foreach($data as $dat){

  $q .= '

       `something` = IF(`id`="'.$dat->id.'","'.$dat->value.'",`something`), 
       `smth2` = IF(`id`="'.$dat->id.'", "'.$dat->value2.'",`smth2`),';

}

$q = substr($q,0,-1);

したがって、1 回のクエリで穴テーブルを更新できます。

于 2013-08-28T15:40:27.173 に答える