Drupal 7 | 使用db_update更新多行

Tau*_*han 4 drupal drupal-7

我有一个像

Array (
    [1] => 85590762,22412382,97998072
    [3] => 22412382 
Run Code Online (Sandbox Code Playgroud)

其中key是item_id,value是我需要针对某项更新的列的值。我可以在循环中使用db_update,但由于性能原因我想避免使用此策略。我想在一个数据库调用中更新所有行。我还认为使用db_query并不是一个好主意。那么,有什么方法可以使用db_update来更新这些行?

根据以上数据,标准的mysql查询将像

update items set sold= 1, users = '85590762,22412382,97998072' Where item_id = 1; 
update items set sold = 1, users = '22412382' Where item_id = 3;
Run Code Online (Sandbox Code Playgroud)

gol*_*007 5

不幸的是你不能那样做。当前并且可能在该功能中,将不支持使用一个db_update在具有不同值的不同行上更新(多个)值。

如果您的数据是这样的:

$for_update = array(
  1 => "85590762,22412382,97998072",
  3 => "22412382",
);
Run Code Online (Sandbox Code Playgroud)

您可以执行以下操作:

情况1:创建一个循环并在每次更新功能时调用:

foreach ($for_update as $key => $value) {
  $fields = array('sold' => 1, 'users' => $value);
  db_update('items')->fields($fields)->condition('item_id', $key, '=')->execute();
}
Run Code Online (Sandbox Code Playgroud)

优点:其他模块可以挂接到您的查询中,可以支持多个数据库驱动程序
缺点:对象初始化很慢,数据库连接/流量很多

案例2:如果使用db_query(),它会更快。

...,因为在这种情况下,没有对象实例化/操作,这会带来一些昂贵的转换。(即:https : //drupal.stackexchange.com/questions/129669/whats-faster-db-query-db-select-or-entityfieldquery

foreach ($for_update as $key => $value) {
  db_query("UPDATE items SET sold = :sold, users = :users WHERE item_id = :item_id",
    array(':sold' => 1, ':users' => $value, ':item_id' => $key)
  );
}
Run Code Online (Sandbox Code Playgroud)

优点:没有对象初始化和操作<,因此更快。
缺点:其他模块无法挂接到您的查询中,您的代码可能在不同的数据库驱动程序下中断,大量的数据库连接/通信

案例3:您也可以使用交易

在某些情况下,这可以提高您的性能。

$transaction = db_transaction();
try {
  foreach ($for_update as $key => $value) {
    $fields = array('sold' => 1, 'users' => $value);
    db_update('items')->fields($fields)->condition('item_id', $key, '=')->execute();
  }
}
catch (Exception $e) {
  $transaction->rollback();
  watchdog_exception('my_type', $e);
}
Run Code Online (Sandbox Code Playgroud)

注意:交易多用,需要什么或什么都不需要时。但是,使用某些数据库驱动程序,您可以优化COMMIT命令。
在情况1中,您得到的是这样的:

UPDATE items SET sold = 1, users = '85590762,22412382,97998072' WHERE item_id = 1;
COMMIT;
UPDATE items SET sold = 1, users = '22412382' WHERE item_id = 3;
COMMIT;
Run Code Online (Sandbox Code Playgroud)

通过事务,您可以执行以下操作:

UPDATE items SET sold = 1, users = '85590762,22412382,97998072' WHERE item_id = 1;
UPDATE items SET sold = 1, users = '22412382' WHERE item_id = 3;
COMMIT;
Run Code Online (Sandbox Code Playgroud)

使用COMMIT,您可以将数据写到最终位置(到长期存储中,直到此为止,它仅在内存中的某个位置或临时位置)。使用回滚时,将删除这些更改。至少我是这样理解的。(即,当我使用sqlite时,它的性能得到了改善。)

与情况1或2 +相同
:优点:如果需要保持一致,可以回滚数据,仅将数据写出一次到驱动器<因此,仅sql“一次”执行IO,大量数据库连接/流量

情况4:或者您可以为此建立一个sql语句:

$ids = array();
$when_then = "";
foreach ($for_update as $key => $value) {
  $ids[] = $key;
  $when_then .= "WHEN $key THEN '$value' ";
}
db_query("UPDATE items
  SET sold = 1, users = CASE item_id 
                    $when_then
                    ELSE users
                    END
  WHERE item_id IN(:item_ids)", array(':item_ids' => $ids));
Run Code Online (Sandbox Code Playgroud)

从这里开始,这可能是最快的方法。
注意:$when_then变量没有最好的解决方案,最好是可以使用参数,或者转义不安全的数据。

优点:服务器和sql之间只有一个请求,冗余数据更少,一个大的sql查询要快得多,然后是许多小的sql
缺点:其他模块无法挂接到您的查询中,您的代码可以在不同的数据库驱动程序下中断

还请注意,在(我认为)PHP 5.3之后,默认情况下,您不能在db_query或PDO中运行多个语句,因为它可以是SQL注入,因此会阻塞。但是,您可以设置此项,但不建议这样做。(PDO支持多个查询(PDO_MYSQL,PDO_MYSQLND)

13-05-2019编辑:
附带一提,我几个月前对中型数据集进行了一些性能测量,以通过db_select和查询一些数据db_query。这些性能的大小是:在运行一次时db_select,可以运行两个db_query。这也适用于db_updatedb_query。该测试查询了一些简单SELECT,没有JOINS,没有花哨的花哨的东西,只有两个条件的列。当然,该度量还包含/包括建立这些查询所需的时间(在两种情况下)。但是请注意,Drupal的编码标准要求使用db_updatedb_deletedb_merge在修改时通常,只有“允许” db_query,而不是db_select操作。