Domanda

So che puoi inserire più righe contemporaneamente, esiste un modo per aggiornare più righe contemporaneamente (come in una query) in MySQL?

Modificare:Ad esempio ho il seguente

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

Voglio combinare tutti i seguenti aggiornamenti in un'unica 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;
È stato utile?

Soluzione

Sì, è possibile: puoi usare INSERT ...SU AGGIORNAMENTO CHIAVE DUPLICATA.

Usando il tuo esempio:

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

Altri suggerimenti

Poiché disponi di valori dinamici, devi utilizzare un IF o CASE per aggiornare le colonne.Diventa un po' brutto, ma dovrebbe funzionare.

Usando il tuo esempio, potresti farlo in questo modo:

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

La domanda è vecchia, ma vorrei ampliare l'argomento con un'altra risposta.

Il punto è che il modo più semplice per ottenerlo è semplicemente racchiudere più query in una transazione.La risposta accettata INSERT ... ON DUPLICATE KEY UPDATE è un bel trucco, ma bisogna essere consapevoli dei suoi svantaggi e limiti:

  • Come detto, se ti capita di lanciare la query con righe le cui chiavi primarie non esistono nella tabella, la query inserisce nuovi record "mezzi".Probabilmente non è quello che vuoi
  • Se hai una tabella con un campo non nullo senza valore predefinito e non vuoi toccare questo campo nella query, otterrai "Field 'fieldname' doesn't have a default value" Avviso MySQL anche se non inserisci una sola riga.Ti metterà nei guai se decidi di essere severo e trasformare gli avvisi di MySQL in eccezioni di runtime nella tua app.

Ho effettuato alcuni test delle prestazioni per tre delle varianti suggerite, inclusa la INSERT ... ON DUPLICATE KEY UPDATE variante, una variante con clausola "caso/quando/allora" e un approccio ingenuo con la transazione.Potresti ottenere il codice Python e i risultati Qui.La conclusione generale è che la variante con l'istruzione case risulta essere due volte più veloce di altre due varianti, ma è piuttosto difficile scrivere un codice corretto e sicuro per l'iniezione, quindi personalmente mi attengo all'approccio più semplice:utilizzando le transazioni.

Modificare: Risultati di Dakusan dimostrare che le mie stime sulle prestazioni non sono del tutto valide.Perfavore guarda questa risposta per un'altra ricerca più elaborata.

Non sono sicuro del motivo per cui un'altra opzione utile non sia ancora menzionata:

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;

Tutto quanto segue si applica a InnoDB.

Sento che conoscere le velocità dei 3 diversi metodi è importante.

Esistono 3 metodi:

  1. INSERIRE:INSERIRE con AGGIORNAMENTO CHIAVE DUPLICATA
  2. TRANSAZIONE:Dove esegui un aggiornamento per ogni record all'interno di una transazione
  3. CASO:In cui hai un caso/quando per ogni record diverso all'interno di un UPDATE

L'ho appena testato e il metodo INSERT lo era 6,7x più veloce per me rispetto al metodo TRANSACTION.Ho provato su un set di 3.000 e 30.000 righe.

Il metodo TRANSACTION deve ancora eseguire ciascuna query individualmente, il che richiede tempo, sebbene raccolga i risultati in memoria, o qualcosa del genere, durante l'esecuzione.Il metodo TRANSACTION è inoltre piuttosto costoso sia nella replica che nei log delle query.

Ancora peggio, il metodo CASE lo era 41,1x più lento del metodo INSERT con 30.000 record (6,1 volte più lento di TRANSACTION).E 75x più lento in MyISAM.I metodi INSERT e CASE hanno raggiunto il pareggio a circa 1.000 record.Anche con 100 record, il metodo CASE è MOLTO più veloce.

Quindi, in generale, ritengo che il metodo INSERT sia il migliore e il più semplice da usare.Le query sono più piccole e più facili da leggere e richiedono solo 1 query di azione.Questo vale sia per InnoDB che per MyISAM.

Roba bonus:

La soluzione al problema INSERT con campi non predefiniti è disattivare temporaneamente le modalità SQL pertinenti: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TA‌​BLES",""),"STRICT_AL‌​L_TABLES","").Assicurati di salvare il file sql_mode prima se hai intenzione di ripristinarlo.

Per quanto riguarda altri commenti che ho visto dire che l'auto_increment aumenta utilizzando il metodo INSERT, ho provato anche quello e sembra che non sia così.

Il codice per eseguire i test è il seguente.Inoltre genera file .SQL per rimuovere il sovraccarico dell'interprete 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'

Questo dovrebbe funzionare per te.

C'è un riferimento in il manuale MySQL per più tabelle.

Utilizza una tabella temporanea

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

Puoi creare un alias della stessa tabella per darti gli ID che desideri inserire (se stai eseguendo un aggiornamento riga per riga:

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

Inoltre, dovrebbe sembrare ovvio che puoi aggiornare anche da altre tabelle.In questo caso, l'aggiornamento funge anche da istruzione "SELECT", fornendo i dati dalla tabella che stai specificando.Stai dichiarando esplicitamente nella tua query i valori di aggiornamento, quindi la seconda tabella non è influenzata.

Perché nessuno lo menziona più istruzioni in una query?

In php, usi multi_query metodo dell'istanza mysqli.

Dal manuale php

MySQL consente facoltativamente di avere più istruzioni in una stringa di istruzioni.L'invio di più istruzioni contemporaneamente riduce i viaggi di andata e ritorno client-server ma richiede una gestione speciale.

Ecco il risultato confrontato con altri 3 metodi nell'aggiornamento 30.000 raw.È possibile trovare il codice Qui che si basa sulla risposta di @Dakusan

Transazione:5.5194580554962
Inserire:0,20669293403625
Caso:16.474853992462
Multiplo:0,0412278175354

Come puoi vedere, la query con più istruzioni è più efficiente della risposta più alta.

Se ricevi un messaggio di errore come questo:

PHP Warning:  Error while sending SET_OPTION packet

Potrebbe essere necessario aumentare il max_allowed_packet nel file di configurazione di mysql che è nella mia macchina /etc/mysql/my.cnf e quindi riavviare mysqld.

Potresti anche essere interessato a utilizzare i join sugli aggiornamenti, anche questo è possibile.

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.

Modificare:Se i valori che stai aggiornando non provengono da qualche altra parte del database, dovrai inviare più query di aggiornamento.

Esiste un'impostazione che puoi modificare chiamata "multi istruzione" che disabilita il "meccanismo di sicurezza" di MySQL implementato per impedire (più di un) comando di injection.Tipico dell'implementazione "brillante" di MySQL, impedisce anche all'utente di eseguire query efficienti.

Qui (http://dev.mysql.com/doc/refman/5.1/en/mysql-set-server-option.html) sono alcune informazioni sull'implementazione C dell'impostazione.

Se stai usando PHP, puoi usare mysqli per fare più istruzioni (penso che php sia stato fornito con mysqli ormai da un po')

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

Spero che aiuti.

utilizzo

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

Notare che:

  • id deve essere una chiave primaria univoca
  • Se si utilizzano le chiavi estere per fare riferimento alla tabella, sostituire le eliminazioni, quindi questo potrebbe causare un errore

Quanto segue aggiornerà tutte le righe in una tabella

Update Table Set
Column1 = 'New Value'

Il successivo aggiornerà tutte le righe in cui il valore di Colonna2 è superiore a 5

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

C'è tutto SconosciutoQuesto è l'esempio di aggiornamento di più di una tabella

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

Sì... è possibile utilizzare l'istruzione sql INSERT ON DUPLICATE KEY UPDATE..sintassi:Inserisci in table_name (a, b, c) valori (1,2,3), (4,5,6) sull'aggiornamento della chiave duplicata a = valori (a), b = valori (b), c = valori (c)

Con PHP ho fatto questo.Usa il punto e virgola, dividilo in un array e quindi invialo tramite loop.

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

Questo dovrebbe ottenere ciò che stai cercando.Basta aggiungere altri ID.L'ho testato.

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

// Lo stai semplicemente costruendo in stile 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);

Quindi puoi aggiornare la tabella dei fori con una query

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top