Вопрос

Я знаю, что вы можете вставить несколько строк одновременно. Есть ли способ одновременно обновить несколько строк (например, в одном запросе) в MySQL?

Редактировать:Например, у меня есть следующее

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

Я хочу объединить все следующие обновления в один запрос

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;
Это было полезно?

Решение

Да, это возможно – вы можете использовать INSERT...ОБНОВЛЕНИЕ ДУБЛИКАЦИОННОГО КЛЮЧА.

Используя ваш пример:

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

Другие советы

Поскольку у вас есть динамические значения, вам необходимо использовать IF или CASE для обновляемых столбцов.Это становится немного некрасиво, но это должно работать.

Используя ваш пример, вы можете сделать это так:

UPDATE table SET Col1 = CASE id 
                          WHEN 1 THEN 1 
                          WHEN 2 THEN 2 
                          WHEN 4 THEN 10 
                          ELSE Col1 
                        END, 
                 Col2 = CASE id 
                          WHEN 3 THEN 3 
                          WHEN 4 THEN 12 
                          ELSE Col2 
                        END
             WHERE id IN (1, 2, 3, 4);

Вопрос старый, но я хотел бы расширить тему еще одним ответом.

Я хочу сказать, что самый простой способ добиться этого — просто объединить несколько запросов в транзакцию.Принятый ответ INSERT ... ON DUPLICATE KEY UPDATE — хороший хак, но следует знать о его недостатках и ограничениях:

  • Как уже говорилось, если вы запустите запрос со строками, первичные ключи которых не существуют в таблице, запрос вставит новые «недоделанные» записи.Наверное, это не то, что ты хочешь
  • Если у вас есть таблица с непустым полем без значения по умолчанию и вы не хотите трогать это поле в запросе, вы получите "Field 'fieldname' doesn't have a default value" Предупреждение MySQL, даже если вы вообще не вставляете ни одной строки.У вас могут возникнуть проблемы, если вы решите проявить строгость и превратить предупреждения MySQL в исключения времени выполнения в вашем приложении.

Я провел несколько тестов производительности для трех предложенных вариантов, включая INSERT ... ON DUPLICATE KEY UPDATE вариант, вариант с предложением «случай/когда/тогда» и наивный подход с транзакцией.Вы можете получить код Python и результаты здесь.Общий вывод таков, что вариант с оператором case оказывается в два раза быстрее двух других вариантов, но написать для него корректный и безопасный для внедрения код довольно сложно, поэтому лично я придерживаюсь самого простого подхода:с помощью транзакций.

Редактировать: Выводы Дакусан доказать, что мои оценки производительности не совсем верны.Пожалуйста, посмотри этот ответ для другого, более тщательного исследования.

Не знаю, почему еще не упомянута еще одна полезная опция:

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;

Все нижеследующее относится к InnoDB.

Я считаю, что важно знать скорость трех разных методов.

Есть 3 метода:

  1. ВСТАВЛЯТЬ:ВСТАВИТЬ с ОБНОВЛЕНИЕМ ДУБЛИКАЦИОННОГО КЛЮЧА
  2. СДЕЛКА:Когда вы обновляете каждую запись в транзакции
  3. СЛУЧАЙ:В каком случае/когда для каждой отдельной записи в UPDATE

Я только что проверил это, и метод INSERT был 6,7x для меня быстрее, чем метод TRANSACTION.Я попробовал набор из 3000 и 30 000 строк.

Методу TRANSACTION по-прежнему приходится запускать каждый отдельный запрос, что требует времени, хотя во время выполнения он группирует результаты в памяти или что-то в этом роде.Метод TRANSACTION также довольно дорог как для журналов репликации, так и для журналов запросов.

Хуже того, метод CASE был 41,1x медленнее, чем метод INSERT с 30 000 записей (в 6,1 раза медленнее, чем TRANSACTION).И 75x медленнее в MyISAM.Методы INSERT и CASE достигли уровня около 1000 записей.Даже при 100 записях метод CASE работает НЕМНОГО быстрее.

В общем, я считаю, что метод INSERT является лучшим и самым простым в использовании.Запросы меньше по размеру, их легче читать, и они занимают всего один запрос действия.Это относится как к InnoDB, так и к MyISAM.

Бонусные вещи:

Решением проблемы INSERT с полем, отличным от поля по умолчанию, является временное отключение соответствующих режимов SQL: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TA‌​BLES",""),"STRICT_AL‌​L_TABLES","").Обязательно сохраните sql_mode сначала, если вы планируете вернуть его.

Что касается других комментариев, которые я видел, в которых говорится, что auto_increment увеличивается с использованием метода INSERT, я тоже это проверял, и, похоже, это не так.

Код для запуска тестов выглядит следующим образом.Он также выводит файлы .SQL для устранения накладных расходов интерпретатора 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).';');
}
UPDATE table1, table2 SET table1.col1='value', table2.col1='value' WHERE table1.col3='567' AND table2.col6='567'

Это должно сработать для тебя.

Есть ссылка в руководство по MySQL для нескольких таблиц.

Используйте временную таблицу

// 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`");
}

Вы можете использовать псевдоним той же таблицы, чтобы дать вам идентификатор, который вы хотите вставить (если вы выполняете обновление построчно:

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

Кроме того, должно показаться очевидным, что вы также можете обновлять данные из других таблиц.В этом случае обновление выполняет функцию оператора «SELECT», предоставляя вам данные из указанной вами таблицы.В своем запросе вы явно указываете значения обновления, поэтому вторая таблица не затрагивается.

Почему никто не упоминает несколько операторов в одном запросе?

В php вы используете multi_query метод экземпляра mysqli.

Из руководство по PHP

MySQL опционально позволяет иметь несколько операторов в одной строке операторов.Одновременная отправка нескольких операторов сокращает количество обращений между клиентом и сервером, но требует специальной обработки.

Вот результат по сравнению с тремя другими методами в обновлении 30 000 Raw.Код можно найти здесь что основано на ответе @Dakusan

Сделка:5.5194580554962
Вставлять:0,20669293403625
Случай:16.474853992462
Мульти:0,0412278175354

Как видите, запрос с несколькими операторами более эффективен, чем самый высокий ответ.

Если вы получили такое сообщение об ошибке:

PHP Warning:  Error while sending SET_OPTION packet

Возможно, вам придется увеличить max_allowed_packet в файле конфигурации mysql, который на моей машине /etc/mysql/my.cnf а затем перезапустите mysqld.

Вас также может заинтересовать использование объединений при обновлениях, что тоже возможно.

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.

Редактировать:Если обновляемые значения не поступают откуда-то еще в базе данных, вам придется выполнить несколько запросов на обновление.

Существует параметр, который вы можете изменить, называемый «мульти-оператор», который отключает «механизм безопасности» 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();

Надеюсь, это поможет.

использовать

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

Пожалуйста, обрати внимание:

  • id должен быть первичным уникальным ключом
  • if you use foreign keys to reference the table, REPLACE deletes then inserts, so this might cause an error

Следующее обновит все строки в одной таблице.

Update Table Set
Column1 = 'New Value'

Следующий обновит все строки, где значение столбца 2 больше 5.

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

Есть все Неизвестные технологиипример обновления более чем одной таблицы

UPDATE table1, table2 SET
table1.col1 = 'value',
table2.col1 = 'value'
WHERE
table1.col3 = '567'
AND table2.col6='567'

Да... это возможно с помощью инструкции INSERT ON DUPLICATE KEY UPDATE sql.синтаксис:Вставьте в Table_Name (A, B, C) Значения (1,2,3), (4,5,6) на дубликате

С PHP я сделал это.Используйте точку с запятой, разделите его на массив и затем отправьте через цикл.

$con = new mysqli('localhost','user1','password','my_database');
$batchUpdate = true; /*You can choose between batch and single query */
$queryIn_arr = explode(";", $queryIn);

if($batchUpdate)    /* My SQL prevents multiple insert*/
{
    foreach($queryIn_arr as $qr)
    {
        if(strlen($qr)>3)
        {
            //echo '<br>Sending data to SQL1:<br>'.$qr.'</br>';
            $result = $conn->query($qr);
        }

    }
}
else
{
    $result = $conn->query($queryIn);
}
$con->close();
UPDATE tableName SET col1='000' WHERE id='3' OR id='5'

Это должно достичь того, что вы ищете.Просто добавьте больше идентификаторов.Я проверил это.

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

Таким образом, вы можете обновить таблицу отверстий одним запросом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top