Frage

Ich weiß, dass Sie einfügen können Sie mehrere Zeilen auf einmal, gibt es eine Möglichkeit zum aktualisieren mehrerer Zeilen auf einmal (in, in einer Abfrage) in MySQL?

Edit:Ich habe zum Beispiel die folgenden

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

Ich will kombinieren alle die folgenden Updates in einem query

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;
War es hilfreich?

Lösung

Ja, das ist möglich - können Sie INSERT ...ON DUPLICATE KEY UPDATE.

Mit Ihrem Beispiel:

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

Andere Tipps

Da haben Sie dynamische Werte, die Sie brauchen, um verwenden Sie eine IF-oder CASE für die Spalten aktualisiert werden.Es wird ein bisschen hässlich, aber es sollte funktionieren.

Mit Ihrem Beispiel, Sie könnten es tun, wie:

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

Die Frage ist alt, aber ich würde gerne verlängern sich dem Thema mit einer anderen Antwort.

Mein Punkt ist, der einfachste Weg das zu erreichen ist es einfach zu wrap mehrere Abfragen mit einer Transaktion.Die akzeptierte Antwort INSERT ... ON DUPLICATE KEY UPDATE ist ein netter hack, aber man sollte sich bewusst sein, die Nachteile und Einschränkungen:

  • Wie gesagt, wenn Sie geschehen, starten Sie die Abfrage mit Zeilen, deren primäre Schlüssel nicht in der Tabelle vorhanden sind, wird die Abfrage fügt neue "half-baked" - Einträge.Wahrscheinlich ist es nicht, was Sie wollen
  • Wenn Sie haben eine Tabelle mit einer nicht-null-Feld ohne Standardwert sind und nicht möchten, berühren Sie dieses Feld in der Abfrage, die Sie erhalten "Field 'fieldname' doesn't have a default value" MySQL-Warnung, auch wenn Sie kein einfügen einer einzelnen Zeile an alle.Es wird Sie in Schwierigkeiten geraten, wenn Sie sich entscheiden, streng zu sein, und drehen Sie mysql Warnungen, die in die Laufzeit-exceptions in your app.

Ich habe einige performance-tests für die drei vorgeschlagenen Varianten, einschließlich der INSERT ... ON DUPLICATE KEY UPDATE Variante, eine Variante mit "Fall / wenn / dann" - Klausel und einen naiven Ansatz mit der Transaktion.Sie können den python-code und Ergebnisse hier.Die Allgemeine Schlussfolgerung ist, dass die Variante mit der case-Anweisung, entpuppt sich doppelt so schnell wie die beiden anderen Varianten, aber es ist ziemlich schwer zu schreiben, korrigieren und Injektion-safe-code, so dass ich persönlich stick der einfachste Ansatz:Transaktionen verwenden.

Edit: Ergebnisse Dakusan beweisen, dass meine Leistung Einschätzungen sind nicht ganz gültig.Finden diese Antwort für eine andere, aufwändigere recherche.

Nicht sicher, warum eine weitere nützliche option ist noch nicht erwähnt:

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;

Alle der folgenden gilt für InnoDB.

Ich fühle mich zu wissen, das Geschwindigkeit von 3 verschiedenen Methoden ist wichtig.

Es gibt 3 Methoden:

  1. EINFÜGEN:LEGEN Sie mit ON DUPLICATE KEY UPDATE
  2. TRANSAKTION:Wo man ein update für jeden Datensatz innerhalb einer Transaktion
  3. FALL:In dem Sie einen Fall/wenn für jeden Datensatz in einer UPDATE

Ich habe gerade getestet, und die INSERT-Methode war 6,7 x schneller für mich als die TRANSAKTION Methode.Ich habe versucht, auf eine Reihe von sowohl 3.000 und 30.000 Zeilen.

Die TRANSAKTION Methode noch ausgeführt werden jeweils einzeln Abfrage, was Zeit in Anspruch nimmt, obwohl es batches die Ergebnisse im Speicher, oder etwas, während der Ausführung.Die TRANSAKTION Methode ist auch ziemlich teuer, sowohl Replikations-und query-logs.

Schlimmer noch, die FALL-Methode war 41.1 x langsamer als die INSERT-Methode w/ 30,000 Datensätzen (6.1 x langsamer als TRANSAKTION).Und 75x langsamer als MyISAM.INSERT und CASE-Methoden brach auch auf ~1.000 Datensätze.Selbst bei 100 Datensätzen, die FALL-Methode ist KAUM schneller.

So im Allgemeinen, ich fühle die INSERT-Methode ist die beste und am einfachsten zu verwenden.Die Abfragen sind kleiner und einfacher zu Lesen und nehmen nur 1 Abfrage der Aktion.Dies gilt sowohl für InnoDB und MyISAM.

Bonus stuff:

Die Lösung für das EINFÜGEN von nicht-Standard-Feld problem vorübergehend zu deaktivieren, die entsprechenden SQL-Modi: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TA‌​BLES",""),"STRICT_AL‌​L_TABLES","").Stellen Sie sicher, dass, um die sql_mode erste, wenn Sie planen, Rückkehr es.

Für andere Kommentare, die ich gesehen habe, die sagen, die auto_increment geht mit der INSERT-Methode, die ich getestet habe das auch und es scheint nicht der Fall ist.

Code zum ausführen des tests ist wie folgt.Es auch Ausgänge .SQL-Dateien zu entfernen php-interpreter-overhead

<?
//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'

Diese Arbeit sollte für ya.

Es ist eine Referenz in das MySQL-Handbuch für mehrere Tabellen.

Die Verwendung einer temporären Tabelle

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

Sie können alias-die gleiche Tabelle geben Sie die id, die Sie einfügen möchten, indem Sie (wenn Sie eine Zeile für Zeile zu aktualisieren:

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

Zusätzlich, Es sollte einleuchten, dass können Sie das update auch aus anderen Tabellen als gut.In diesem Fall, das update dient auch als ein "SELECT" - Anweisung, so dass Sie die Daten aus der Tabelle angegeben.Sie sind explizit in Ihrer Abfrage die Werte zu aktualisieren, so dass die zweite Tabelle ist unberührt.

Warum hat niemand erwähnt mehrere Anweisungen in einer Abfrage?

In php verwenden Sie multi_query Methode der mysqli-Instanz.

Aus der php-Handbuch

MySQL wahlweise können mehrere Anweisungen in einer-Anweisung-Zeichenfolge.Senden Sie mehrere Anweisungen auf einmal reduziert der client-server-round-trips, sondern erfordert eine spezielle Handhabung.

Hier ist das Ergebnis im Vergleich zu den anderen 3 Methoden-update 30,000 raw.Code hier basierend auf der Antwort von @Dakusan

Transaktion:5.5194580554962
Einfügen:0.20669293403625
Fall:16.474853992462
Multi:0.0412278175354

Wie Sie sehen können, mehrere Anweisungen Abfrage ist effizienter als das höchste Antwort.

Wenn Sie erhalten die Fehlermeldung wie diese:

PHP Warning:  Error while sending SET_OPTION packet

Möglicherweise müssen Sie erhöhen Sie die max_allowed_packet in der mysql-config-Datei, die bei meiner Maschine ist /etc/mysql/my.cnf und dann starten Sie mysqld.

Sie könnten auch interessiert sein an mithilfe von Verknüpfungen auf updates, die möglich ist.

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.

Edit:Wenn die Werte, die Sie aktualisieren können, kommen nicht von irgendwo anders in der Datenbank, müssen Sie geben Sie mehrere update-Abfragen.

Es ist eine Einstellung, die Sie ändern können, genannt "multi-Erklärung', die deaktiviert MySQL die "Sicherheits-Mechanismus" umgesetzt zu verhindern (mehr als einer) Injektions-Befehl.Die typische MySQL ' s "brilliant" - Implementierung, es verhindert auch, dass Benutzer tun, effiziente Abfragen.

Hier (http://dev.mysql.com/doc/refman/5.1/en/mysql-set-server-option.html) einige Infos über die C-Implementierung der Einstellung.

Wenn Sie PHP verwenden, können Sie mysqli zu tun, multi-Anweisungen (ich denke, php ausgeliefert hat mit mysqli für eine Weile jetzt)

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

Hoffe, das hilft.

verwenden

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

Bitte beachten Sie:

  • id primary-unique key
  • wenn Sie Fremdschlüssel zu Referenz der Tabelle, ERSETZEN löscht eingefügt, so könnte dies einen Fehler verursachen

Das folgende wird für Sie aktualisieren alle Zeilen in einer Tabelle

Update Table Set
Column1 = 'New Value'

Die nächste aktualisieren Sie alle Zeilen, in denen der Wert von Column2 mehr als 5

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

Es ist alles Unkwntech's Beispiel zu aktualisieren, die mehr als eine Tabelle

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

Ja ..es ist möglich, die Verwendung von INSERT ON DUPLICATE KEY UPDATE sql-Anweisung..syntax:INSERT INTO table_name (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c)

Mit PHP habe ich dies getan.Verwenden Sie Semikolon, teilen Sie es in ein array, und senden Sie dann über die Schleife.

$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'

Diese sollte erreichen, was you ' r looking for.Fügen Sie einfach mehr id.Ich habe es getestet.

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

// Sie bauen es in php wie

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

So können Sie die update-Loch-Tabelle mit einer Abfrage

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top