سؤال

أعلم أنه يمكنك إدراج صفوف متعددة في وقت واحد، هل هناك طريقة لتحديث صفوف متعددة في وقت واحد (كما هو الحال في استعلام واحد) في 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 متغير، متغير مع شرط "الحالة / متى / ثم" ونهج ساذج مع المعاملة.قد تحصل على كود بايثون والنتائج هنا.الاستنتاج العام هو أن المتغير الذي يحتوي على بيان الحالة تبين أنه أسرع مرتين من المتغيرين الآخرين، ولكن من الصعب جدًا كتابة كود صحيح وآمن للحقن له، لذلك أنا شخصيًا متمسك بأبسط نهج:باستخدام المعاملات.

يحرر: النتائج داكوسان إثبات أن تقديرات أدائي ليست صحيحة تمامًا.لطفا أنظر هذه الإجابة لبحث آخر أكثر تفصيلا.

لست متأكدًا من سبب عدم ذكر خيار مفيد آخر بعد:

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. قضية:حيث لديك حالة/متى لكل سجل مختلف ضمن تحديث

لقد اختبرت هذا للتو، وكانت طريقة INSERT 6.7x أسرع بالنسبة لي من طريقة المعاملة.لقد حاولت استخدام مجموعة مكونة من 3000 و30000 صف.

لا يزال يتعين على أسلوب المعاملة تشغيل كل استعلام على حدة، الأمر الذي يستغرق وقتًا، على الرغم من أنه يقوم بتجميع النتائج في الذاكرة، أو شيء من هذا القبيل، أثناء التنفيذ.يعد أسلوب TRANSACTION أيضًا مكلفًا جدًا في كل من سجلات النسخ المتماثل والاستعلام.

والأسوأ من ذلك أن طريقة CASE كانت كذلك 41.1x أبطأ من أسلوب INSERT مع 30.000 سجل (6.1x أبطأ من 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 أولاً إذا كنت تخطط لإعادته.

أما بالنسبة للتعليقات الأخرى التي رأيتها والتي تشير إلى أن الزيادة التلقائية ترتفع باستخدام طريقة 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 خام.يمكن العثور على الكود هنا والذي يعتمد على إجابة @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);

يرجى الملاحظة:

  • يجب أن يكون المعرف مفتاحًا فريدًا أساسيًا
  • إذا كنت تستخدم مفاتيح أجنبية للإشارة إلى الجدول ، فاستبدل الحذف ثم إدراج ، لذلك قد يتسبب ذلك في حدوث خطأ

سيتم تحديث ما يلي كافة الصفوف في جدول واحد

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'

نعم .. من الممكن استخدام عبارة INSERT ON DUPLICATE KEY UPDATE sql ..بناء الجملة:أدخل في Table_Name (A ، B ، C) القيم (1،2،3) ، (4،5،6) على تحديث مفتاح مكرر A = القيم (أ) ، B = القيم (B) ، C = القيم (C)

مع 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