【问题标题】:Drupal 7 | update multiple rows using db_updateDrupal 7 |使用 db_update 更新多行
【发布时间】:2014-09-03 14:08:11
【问题描述】:

我有一个像

这样的数组
Array (
    [1] => 85590762,22412382,97998072
    [3] => 22412382 

)

其中 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;

【问题讨论】:

    标签: drupal drupal-7


    【解决方案1】:

    很遗憾,您不能这样做。目前并且可能在该功能中,将不支持使用一个 db_update 更新具有不同值的不同行上的(多个)值。

    如果你的数据是这样的:

    $for_update = array(
      1 => "85590762,22412382,97998072",
      3 => "22412382",
    );
    

    你可以这样做:

    CASE 1:创建循环,每次调用update函数:

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

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

    案例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)
      );
    }
    

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

    案例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);
    }
    

    注意:事务主要用于当你想要一切或什么都不想要的时候。但是使用一些 DB 驱动程序,您可以优化 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;
    

    通过交易,您可以执行以下操作:

    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;
    

    使用 COMMIT,您可以将数据写入最终位置(长期存储,在此之前,它只是内存中的某个位置或临时位置)。当您使用回滚时,您会放弃这些更改。至少我是这样理解的。 (即,当我使用 sqlite 时,我得到了性能提升。)

    与案例 1 或 2 相同 +
    优点:如果你需要保持一致,你可以回滚你的数据,你只写一次数据到驱动器

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

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

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

    另外请注意,在(我认为)PHP 5.3 之后,默认情况下您不能在 db_query 或 PDO 中运行多个语句,因为它可能是 SQL 注入,所以它会阻塞。但是你可以设置这个,但不推荐。 (PDO support for multiple queries (PDO_MYSQL, PDO_MYSQLND))

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

    【讨论】:

    • 这是一个非常高质量的答案!感谢您对交易的精彩解释!
    【解决方案2】:

    使用 BULK UPDATE,您可以使用动态查询 db_update 更新不同行或同一行中的多个值。

    注意:通过批量更新,只能将一个查询发送到服务器,而不是每行更新一个查询。

    语法:

    UPDATE yourTableName
    SET yourUpdateColumnName = 
        (CASE yourConditionColumnName WHEN Value1 THEN 'UpdatedValue'
                                      WHEN Value2 THEN 'UpdatedValue'
         .
         .
         N
         END)
    WHERE yourConditionColumnName IN(Value1,Value2,.....N);
    

    有关 UpdateQuery 表达式的更多信息: https://api.drupal.org/api/drupal/includes%21database%21query.inc/function/UpdateQuery%3A%3Aexpression/7.x

    您的数据将是:

    //Create array
    $for_update = [
        1 => "85590762,22412382,97998072",
        3 => "22412382",
    ];
    $item_ids_to_filter = [];
    //Setup when-then conditions
    $when_then = "CASE item_id ";
    foreach ($for_update as $item_id => $users_id) {
        $when_then .= "WHEN {$item_id} THEN '{$users_id}' ";
        $item_ids_to_filter[] = $item_id;
    }
    $when_then .= "END";
    //Execute query and update the data with db_update
    db_update('items')
    ->expression("users",$when_then)
    ->condition('item_id', $item_ids_to_filter)
    ->execute();
    

    结果生成下一个查询:

    UPDATE items
    SET users =
           (CASE item_id =  WHEN 1 THEN '85590762,22412382,97998072'
                            WHEN 3 THEN '22412382'
            END)
    WHERE (item_id IN  ('1', '3'))
    

    【讨论】:

    • 请注意,“复杂(卷曲)语法”仍然没有转义任何内容。
    【解决方案3】:

    我认为你可以做类似的事情

    $query = db_udpate('table')->fields(array('field_1','field_2','field_3'));
      foreach ($fields as $record) {
        $query->values($record);
      }
    return $query->execute();
    

    其中 $fields = 数组('field_1' => VALUE,'field_2' => VALUE)

    【讨论】:

    • 感谢 Lokisho,这似乎可行,但没有条件更新。
    • Drupal 7 中的 UpdateQuery 对象没有 values() 方法,请参阅:api.drupal.org/api/drupal/includes!database!query.inc/class/…
    • 你真的试过这个吗?该技术适用于db_insert,但正如@acrosman 所说,db_update 不会为您提供values 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 2014-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    相关资源
    最近更新 更多