อัพเดทหลายครั้งใน MySQL


388

ฉันรู้ว่าคุณสามารถแทรกหลายแถวพร้อมกันมีวิธีอัปเดตหลายแถวพร้อมกัน (เช่นเดียวกับในแบบสอบถามเดียว) ใน 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;

คำตอบ:


651

ใช่เป็นไปได้ - คุณสามารถใช้ 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);

22
หากไม่มีการซ้ำซ้อนฉันไม่ต้องการแทรกแถวนั้น id ควรทำอย่างไร เพราะฉันดึงข้อมูลจากเว็บไซต์อื่นซึ่งดูแลตารางด้วยรหัส ฉันกำลังแทรกค่าที่เกี่ยวกับรหัสนั้น หากเว็บไซต์มีบันทึกใหม่ฉันจะจบลงด้วยการแทรกรหัสและนับเฉพาะข้อมูลอื่น ๆ เท่านั้น ถ้าหากว่ามี ID สำหรับรายการก็ควรอัพเดทอย่างอื่นก็ควรข้าม ฉันควรทำอะไร?
Jayapal Chandran

33
หมายเหตุ: คำตอบนี้ถือว่า ID เป็นคีย์หลัก
JM4

11
@JayapalChandran คุณควรใช้ INSERT IGNORE พร้อมกับ ON DUPLICATE KEY UPDATE dev.mysql.com/doc/refman/5.5/en/insert.html
Haralan Dobrev

16
@HalalanDobrev การใช้ INSERT IGNORE ยังคงแทรกระเบียนที่ไม่ซ้ำกัน ซึ่งจายาปาลต้องการหลีกเลี่ยง INSERT IGNORE จะเปลี่ยนข้อผิดพลาดเป็นคำเตือน :( stackoverflow.com/questions/548541/…
Takehiro Adachi

2
คำตอบนี้ถือว่ารหัสเป็นกุญแจสำคัญที่ไม่ซ้ำกัน (สามารถเป็นหลักตามที่คนอื่นพูด) แต่ที่สำคัญกว่านั้นคือมันจะถือว่าไม่มีกุญแจที่ไม่ซ้ำอื่น ๆ หากมีก็สามารถโยนประแจในการทำงาน
Steve Horvath

129

เนื่องจากคุณมีค่าแบบไดนามิกคุณต้องใช้ IF หรือ CASE สำหรับคอลัมน์ที่จะอัปเดต มันน่าเกลียด แต่มันก็ใช้ได้

จากตัวอย่างของคุณคุณสามารถทำได้เช่น:

ตารางอัพเดต SET Col1 = CASE id 
                          เมื่อ 1 แล้ว 1 
                          เมื่อ 2 แล้ว 2 
                          เมื่อ 4 และ 10 แล้ว 
                          ELSE Col1 
                        จบ 
                 Col2 = รหัสเคส 
                          เมื่อ 3 และ 3 
                          เมื่อ 4 และ 12 
                          ELSE Col2 
                        END
             WHERE ID (1, 2, 3, 4);

อาจจะไม่ค่อยเขียนการอัปเดตแบบไดนามิก แต่ดูน่าสนใจที่ฟังก์ชั่นของตัวเครื่อง ...
me_

1
@ user2536953 สามารถใช้กับการอัพเดตแบบไดนามิกได้เช่นกัน ตัวอย่างเช่นฉันใช้โซลูชันนั้นในลูปใน php:$commandTxt = 'UPDATE operations SET chunk_finished = CASE id '; foreach ($blockOperationChecked as $operationID => $operationChecked) $commandTxt .= " WHEN $operationID THEN $operationChecked "; $commandTxt .= 'ELSE id END WHERE id IN ('.implode(', ', array_keys(blockOperationChecked )).');';
Boolean_Type

86

คำถามเก่า แต่ฉันต้องการขยายหัวข้อด้วยคำตอบอื่น

ประเด็นของฉันคือวิธีที่ง่ายที่สุดในการบรรลุเป้าหมายคือเพียงห่อคำสั่งหลายรายการด้วยธุรกรรม คำตอบที่ได้รับการยอมรับINSERT ... ON DUPLICATE KEY UPDATEคือแฮ็คที่ดี แต่ก็ควรระวังข้อเสียและข้อ จำกัด ของมัน

  • ตามที่ได้กล่าวไว้หากคุณเปิดใช้คิวรีด้วยแถวที่ไม่มีคีย์หลักอยู่ในตารางเคียวรีจะแทรกเร็กคอร์ด "ครึ่งอบ" ใหม่ อาจไม่ใช่สิ่งที่คุณต้องการ
  • หากคุณมีตารางที่มีเขตข้อมูลไม่เป็นค่าว่างโดยไม่มีค่าเริ่มต้นและไม่ต้องการแตะเขตข้อมูลนี้ในแบบสอบถามคุณจะได้"Field 'fieldname' doesn't have a default value"รับคำเตือน MySQL แม้ว่าคุณจะไม่ได้แทรกแถวเดียวเลย มันจะทำให้คุณมีปัญหาหากคุณตัดสินใจที่จะเข้มงวดและเปลี่ยนคำเตือน mysql เป็นข้อยกเว้นรันไทม์ในแอปของคุณ

ฉันทำการทดสอบประสิทธิภาพสำหรับตัวแปรที่แนะนำสามชุดรวมถึงINSERT ... ON DUPLICATE KEY UPDATEชุดตัวเลือกชุดตัวเลือก "case / when / then" clause และแนวทางที่ไร้เดียงสากับธุรกรรม คุณอาจจะได้รับรหัสหลามและผลที่นี่ ข้อสรุปโดยรวมคือตัวแปรที่มีคำสั่ง case กลายเป็นสองเท่าเร็วกว่าสองตัวแปรอื่น ๆ แต่มันค่อนข้างยากที่จะเขียนรหัสที่ถูกต้องและปลอดภัยสำหรับการฉีดดังนั้นฉันจึงใช้วิธีที่ง่ายที่สุด: การใช้ธุรกรรม

แก้ไข:การค้นพบของDakusanพิสูจน์ว่าการประเมินประสิทธิภาพของฉันไม่ถูกต้องนัก โปรดดูคำตอบนี้สำหรับการวิจัยที่ละเอียดและมากกว่านี้


ใช้การทำธุรกรรมเคล็ดลับที่ดีมาก (และเรียบง่าย)!
mTorres

ถ้าตารางของฉันไม่ใช่ประเภท InnoDB ล่ะ
TomeeNS

1
ใครบางคนสามารถให้ลิงก์ไปยังรายการที่จะทำเช่นนี้? และ / หรือรหัสสำหรับรหัสการฉีดที่ปลอดภัยสำหรับชุดตัวเลือกพร้อมคำสั่ง case หรือไม่
François M.

1
ฉันค้นหาข้อมูลที่ให้เกี่ยวกับความเร็วในโพสต์นี้ผิด ฉันเขียนเกี่ยวกับเรื่องนี้ในโพสต์ด้านล่าง stackoverflow.com/questions/3432/multiple-updates-in-mysql/…
Dakusan

1
@Dakusan คำตอบที่ดี ขอบคุณมากสำหรับการขยายความคิดเห็นและแก้ไขผลลัพธ์ของฉัน
Roman Imankulov

72

ไม่แน่ใจว่าทำไมตัวเลือกที่มีประโยชน์อื่นยังไม่ได้กล่าวถึง:

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;

4
นี่คือสิ่งที่ดีที่สุด. โดยเฉพาะอย่างยิ่งถ้าคุณกำลังดึงค่าสำหรับการปรับปรุงจากแบบสอบถาม SQL อื่นที่ฉันทำ
v010dya

1
นี่เป็นสิ่งที่ยอดเยี่ยมสำหรับการอัพเดทในตารางที่มีคอลัมน์จำนวนมาก ฉันอาจจะใช้การค้นหานี้มากในอนาคต ขอบคุณ!
Casper Wilkes

ฉันลองใช้แบบสอบถามประเภทนี้แล้ว แต่เมื่อระเบียนถึงขอบเขตเซิร์ฟเวอร์ 30k หยุดทำงาน มีวิธีแก้ปัญหาอื่น ๆ อีกไหม?
Bhavin Chauhan

มันดูดีมาก ฉันจะลองรวมสิ่งนี้กับส่วนคำสั่ง WHERE ที่ไม่มีการอัปเดตคีย์หลัก แต่ใช้เพื่อระบุคอลัมน์ที่จะเปลี่ยนแปลง
nl-x

@BhavinChauhan คุณลองใช้ตารางชั่วคราวแทนการเข้าร่วมเลือกเพื่อหลีกเลี่ยงปัญหาหรือไม่
nl-x

41

ข้อมูลทั้งหมดต่อไปนี้ใช้กับ InnoDB

ฉันรู้สึกว่ารู้ความเร็วของ 3 วิธีที่แตกต่างกันเป็นสิ่งสำคัญ

มี 3 วิธีคือ

  1. INSERT: INSERT พร้อมการอัปเดตคีย์ ON DUPLICATE
  2. รายการ: ที่คุณทำการปรับปรุงสำหรับแต่ละระเบียนภายในการทำธุรกรรม
  3. กรณี: ในกรณีที่คุณ / เมื่อสำหรับแต่ละระเบียนที่แตกต่างกันภายในการปรับปรุง

ฉันเพิ่งทดสอบสิ่งนี้และวิธี INSERT นั้นเร็วขึ้น6.7เท่าสำหรับฉันกว่าวิธีการทำธุรกรรม ฉันลองชุดทั้ง 3,000 และ 30,000 แถว

วิธีการทำธุรกรรมยังคงต้องเรียกใช้แบบสอบถามแต่ละรายการซึ่งต้องใช้เวลาแม้ว่ามันจะแบทช์ผลลัพธ์ในหน่วยความจำหรือบางสิ่งบางอย่างในขณะที่ดำเนินการ วิธีการทำธุรกรรมก็ค่อนข้างแพงทั้งในการจำลองแบบและบันทึกแบบสอบถาม

แม้เลววิธีกรณีที่ถูก41.1xช้ากว่าวิธีการแทรก w / 30,000 ระเบียน (6.1 เท่าช้ากว่าการทำธุรกรรม) และช้าลง75xใน MyISAM วิธีการแทรกและกรณีทำลายแม้กระทั่งที่ ~ 1,000 ระเบียน แม้จะอยู่ที่ 100 บันทึกวิธีการ CASE นั้นเร็วกว่า

ดังนั้นโดยทั่วไปฉันรู้สึกว่าวิธี INSERT นั้นดีที่สุดและใช้งานง่ายที่สุด แบบสอบถามมีขนาดเล็กและอ่านง่ายขึ้นและใช้แบบสอบถามเพียงหนึ่งคำเท่านั้น สิ่งนี้ใช้กับทั้ง 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 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).';');
}

1
คุณกำลังทำงานของพระเจ้าที่นี่;) ชื่นชมมาก
พริก

การทดสอบประสิทธิภาพระหว่าง GoLang และ PHP โดยใช้ 40k rows บน MariaDB ฉันได้รับ 2 วินาทีใน PHP และมากกว่า 6 วินาทีใน golang .... ดีฉันได้รับแจ้งเสมอว่า GoLang จะทำงานได้เร็วกว่า PHP !!! ดังนั้นฉันเริ่มสงสัยว่าจะปรับปรุงประสิทธิภาพอย่างไร ... การใช้ INSERT ... ON DUPLICATE KEY UPDATE ... ฉันได้ 0.74 วินาทีบน Golang และ 0.86 วินาทีบน PHP !!!!
Diego Favero

1
จุดของรหัสของฉันคือการ จำกัด ผลลัพธ์การกำหนดเวลาเพื่อคำสั่ง SQL อย่างเคร่งครัดไม่ใช่รหัสสำหรับภาษาหรือไลบรารี GoLang และ PHP เป็น 2 ภาษาที่แยกจากกันอย่างสิ้นเชิงสำหรับสิ่งที่แตกต่างอย่างสิ้นเชิง PHP มีความหมายสำหรับสภาพแวดล้อมการเขียนสคริปต์ที่ทำงานครั้งเดียวในเธรดเดียวที่มีการเก็บขยะ จำกัด และ passive GoLang มีความหมายสำหรับการใช้งานแอพพลิเคชั่นที่คอมไพล์ด้วยการรวบรวมขยะจำนวนมากและมัลติเธรดเป็นหนึ่งในคุณสมบัติหลักของภาษา พวกเขาแทบจะไม่แตกต่างกันในแง่ของการใช้ภาษาและเหตุผล [ดำเนินการต่อ]
Dakusan

ดังนั้นเมื่อเรียกใช้การทดสอบของคุณตรวจสอบให้แน่ใจว่าได้ จำกัด การวัดความเร็วเพื่อเรียกใช้ฟังก์ชัน "แบบสอบถาม" อย่างเคร่งครัดสำหรับคำสั่ง SQL การเปรียบเทียบและเพิ่มประสิทธิภาพส่วนอื่น ๆ ของซอร์สโค้ดที่ไม่เคร่งครัดการเรียกเคียวรีนั้นเหมือนกับการเปรียบเทียบแอปเปิ้ลและส้ม หากคุณ จำกัด ผลลัพธ์ของคุณไว้ที่สิ่งนี้ (มีสตริงที่คอมไพล์แล้วและพร้อมจะไป) ผลลัพธ์ก็ควรจะคล้ายกันมาก ความแตกต่างใด ๆ ณ จุดนั้นเป็นความผิดของไลบรารี SQL ของภาษาและไม่จำเป็นต้องเป็นภาษานั้นเอง ในความคิดของฉันโซลูชัน INSERT ON DUPLICATE คือและจะเป็นตัวเลือกที่ดีที่สุดเสมอ [ต่อ]
Dakusan

สำหรับความคิดเห็นของคุณเกี่ยวกับ GoLang นั้นรวดเร็วขึ้นนั่นเป็นคำแถลงที่กว้างขวางอย่างไม่น่าเชื่อซึ่งไม่ได้คำนึงถึงความหลากหลายของคำเตือนหรือความแตกต่างของภาษาเหล่านี้และการออกแบบของพวกเขา Java เป็นภาษาที่ตีความ แต่ฉันค้นพบเมื่อ 15 ปีที่แล้วมันเกือบจะตรงกับ (และบางครั้งอาจเอาชนะ) C ความเร็วในบางสถานการณ์ และ C เป็นภาษาที่คอมไพล์และภาษาระบบระดับต่ำสุดที่พบบ่อยที่สุดนอกจากแอสเซมเบลอร์ ฉันรักสิ่งที่ GoLang กำลังทำอยู่และมีพลังและความลื่นไหลในการเป็นหนึ่งในระบบที่พบได้บ่อยที่สุดและได้รับการปรับให้เหมาะสม [ต่อ]
Dakusan

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

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

สิ่งนี้ควรใช้กับยา

มีการอ้างอิงในคู่มือ MySQLสำหรับหลายตาราง


6

ทำไมไม่มีใครพูดถึงงบหลายในแบบสอบถามหนึ่ง ?

ใน php คุณใช้multi_queryเมธอดของอินสแตนซ์ mysqli

จากคู่มือ php

MySQL อนุญาตให้มีหลายงบในสตริงคำสั่งเดียว การส่งคำสั่งหลายรายการพร้อมกันจะลดการเดินทางไปกลับของไคลเอนต์เซิร์ฟเวอร์ แต่ต้องมีการจัดการเป็นพิเศษ

นี่คือผลลัพธ์เมื่อเปรียบเทียบกับวิธีอื่น ๆ 3 วิธีในการอัพเดต 30,000 raw รหัสสามารถพบได้ที่นี่ซึ่งขึ้นอยู่กับคำตอบจาก @Dakusan

ธุรกรรม: 5.5194580554962
แทรก: 0.20669293403625
กรณี: 16.474853992462
หลาย: 0.0412278175354

อย่างที่คุณเห็นแบบสอบถามหลายรายการมีประสิทธิภาพมากกว่าคำตอบสูงสุด

หากคุณได้รับข้อความแสดงข้อผิดพลาดเช่นนี้:

PHP Warning:  Error while sending SET_OPTION packet

คุณอาจต้องเพิ่มmax_allowed_packetไฟล์ config ใน mysql ซึ่งในเครื่องของฉัน/etc/mysql/my.cnfแล้วรีสตาร์ท mysqld


การเปรียบเทียบด้านล่างทั้งหมดใช้กับการทดสอบ INSERT ฉันเพิ่งรันการทดสอบในสภาพเดียวกันโดยไม่มีการทำธุรกรรมมันช้ากว่า145xใน 300 แถวและ753xช้ากว่า 3000 แถว เริ่มแรกฉันมีแถว 30,000 แถว แต่ฉันไปทำอาหารกลางวันและกลับมาและมันก็ยังคงดำเนินต่อไป สิ่งนี้ทำให้รู้สึกว่าการเรียกใช้แบบสอบถามแต่ละรายการและการล้างข้อมูลแต่ละรายการไปยังฐานข้อมูลนั้นจะมีราคาแพงอย่างน่าขัน โดยเฉพาะอย่างยิ่งกับการจำลองแบบ เปิดการทำธุรกรรม แต่สร้างความแตกต่างใหญ่ 3,000 แถวมันเอา1.5xมากขึ้นและ 30,000 แถว2.34x [ดำเนินการต่อ]
Dakusan

แต่คุณพูดถูกเกี่ยวกับมันอย่างรวดเร็ว (กับการทำธุรกรรม) ทั้งแถว 3,000 และ 30,000 แถวนั้นเร็วกว่าทุกอย่างยกเว้นวิธี INSERT ไม่มีทางที่คุณจะได้รับผลลัพธ์ที่ดีกว่าจากการรัน 1 เคียวรีมากกว่า 30,000 คิวรีแม้ว่าจะถูกแบตช์ในการเรียก MySQL API แบบพิเศษก็ตาม ทำงานเพียง 300 แถวมันเร็วกว่าวิธีอื่น ๆ ทั้งหมด (เพื่อความประหลาดใจของฉัน) ซึ่งตามหลังกราฟโค้งเดียวกับวิธี CASE การอธิบายให้เร็วขึ้นนี้สามารถอธิบายได้ 2 วิธี เป็นคนแรกที่วิธีการแทรกเป็นหลักมักจะแทรก 2 แถวเนื่องจาก "ON DUPLICATE KEY [ต่อ]
Dakusan

UPDATE ทำให้ทั้ง "INSERT" และ "UPDATE" เป็นอีกตัวที่ทำงานน้อยลงในตัวประมวลผล SQL สำหรับการแก้ไขครั้งละ 1 แถวเท่านั้นเนื่องจากการค้นหาดัชนีฉันไม่แน่ใจว่าคุณได้ผลลัพธ์ที่แตกต่างจากฉันอย่างไร แต่การทดสอบเพิ่มเติมของคุณดูแข็งแกร่งจริง ๆ แล้วฉันไม่แน่ใจว่าการจำลองแบบจะจัดการกับการโทรนี้ได้อย่างไรนอกจากนี้ยังใช้งานได้กับการโทร UPDATE เท่านั้นการแทรกการโทรจะเร็วที่สุดเมื่อใช้คำสั่ง INSERT เดียวเสมอ
Dakusan

ฉันทำการอัปเดตครั้งละ 300 ครั้งบนโต๊ะเพื่อแก้ไขข้อผิดพลาดภายในการวนซ้ำซึ่งใช้เวลา 41 วินาที การใส่ข้อความค้นหา UPDATE เดียวกันลงในข้อความเดียวจะ$mysqli->multi_query($sql)ใช้เวลา "0" วินาที อย่างไรก็ตามการสืบค้นที่ตามมาล้มเหลวทำให้ฉันทำให้มันเป็น "โปรแกรม" แยกต่างหาก
Chris K

ขอบคุณ สามารถอัปเดตแถวประมาณ 5k (ไม่ได้ทดสอบมากขึ้น) ในหนึ่งนาทีโดยใช้หลายข้อความค้นหา หากใครบางคนกำลังมองหาโซลูชัน PDO: stackoverflow.com/questions/6346674/…
Scofield

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

หวังว่าจะช่วย


4
สิ่งนี้เหมือนกับการส่งแบบสอบถามแยกต่างหาก ข้อแตกต่างเพียงอย่างเดียวคือคุณส่งได้ทั้งหมดในเครือข่ายแพ็คเก็ตเดียว แต่ UPDATE จะยังคงถูกประมวลผลเป็นข้อความค้นหาแยกต่างหาก ดีกว่าคือการรวมไว้ในธุรกรรมเดียวจากนั้นการเปลี่ยนแปลงจะถูกส่งไปยังตารางทันที
Marki555

3
วิธีการห่อในหนึ่งธุรกรรม? แสดงให้เราเห็นด้วย
TomeeNS

@TomeeNS ใช้mysqli::begin_transaction(..)ก่อนที่จะส่งแบบสอบถามและmysql::commit(..)หลัง หรือใช้START TRANSACTIONเป็นCOMMITคำสั่งแรกและคำสั่งสุดท้ายในแบบสอบถามเอง
Juha Palomäki

3

คุณสามารถนามแฝงตารางเดียวกันเพื่อให้รหัสที่คุณต้องการแทรกโดย (ถ้าคุณทำการอัปเดตแบบแถวต่อแถว:

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

นอกจากนี้ดูเหมือนว่าคุณสามารถอัปเดตจากตารางอื่นได้เช่นกัน ในกรณีนี้การอัพเดตจะเพิ่มเป็นสองเท่าของคำสั่ง "SELECT" ซึ่งจะให้ข้อมูลจากตารางที่คุณระบุ คุณกำลังระบุค่าการอัพเดทอย่างชัดเจนในแบบสอบถามของคุณดังนั้นตารางที่สองจะไม่ได้รับผลกระทบ


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.

แก้ไข: หากค่าที่คุณอัปเดตไม่ได้มาจากที่อื่นในฐานข้อมูลคุณจะต้องออกแบบสอบถามแบบใช้อัปเดตหลายรายการ


1

และตอนนี้วิธีที่ง่าย

update speed m,
    (select 1 as id, 20 as speed union
     select 2 as id, 30 as speed union
     select 99 as id, 10 as speed
        ) t
set m.speed = t.speed where t.id=m.id

-1

ใช้

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

โปรดทราบ:

  • id ต้องเป็นคีย์หลักที่ไม่ซ้ำกัน
  • หากคุณใช้ foreign key เพื่ออ้างอิงตาราง REPLACE จะลบการแทรกดังนั้นสิ่งนี้อาจทำให้เกิดข้อผิดพลาด

-3

ต่อไปนี้จะอัปเดตแถวทั้งหมดในตารางเดียว

Update Table Set
Column1 = 'New Value'

แถวถัดไปจะอัปเดตทุกแถวโดยที่ค่าของคอลัมน์ 2 มากกว่า 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'

-3

ใช่ .. มันเป็นไปได้โดยใช้ INSERT ON DUPLICATE KEY UPDATE คำสั่ง sql .. ไวยากรณ์: INSERT INTO table_name (a, b, c) ค่า (1,2,3), (4,5,6) ON DUPLICATE KEY UPDATE a = VALUES (ก) ข = VALUES (ข), c = VALUES (c)


-5
UPDATE tableName SET col1='000' WHERE id='3' OR id='5'

สิ่งนี้ควรจะบรรลุสิ่งที่คุณต้องการ เพียงเพิ่มรหัสเพิ่มเติม ฉันได้ทำการทดสอบแล้ว


-7
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 like

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

ดังนั้นคุณสามารถอัปเดตตารางรูด้วยเคียวรี่เดียว


ฉันไม่ได้ downvote แต่ฉันคิดว่าการคัดค้านคือการทำชุดเมื่อมันไม่จำเป็น (และคุณยังคงทำมันเมื่อคุณกำลังตั้งsomethingไปsomething)
v010dya
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.