Drupal 7 | update multiple rows using db_update

僤鯓⒐⒋嵵緔 提交于 2020-01-02 05:22:07

问题


I have an array like

Array (
    [1] => 85590762,22412382,97998072
    [3] => 22412382 

)

Where key is the item_id and value is the value of a column which I need to update against an item. I can use db_update in a loop but i want to avoid this strategy due to performance. I want to update all the rows in a single db call. Also using db_query I think will not be a good idea. So is there any way using db_update to update these rows?

According to above data, standard mysql queries will be like

update items set sold= 1, users = '85590762,22412382,97998072' Where item_id = 1; 
update items set sold = 1, users = '22412382' Where item_id = 3;

回答1:


Unfortunately you can't do that. Currently and probably in the feature, there won't be support for updating (multiple) values on different rows with different values with one db_update.

If your data is this:

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

You can do these:

CASE 1: Create a loop and call every time the update function:

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

Pros: other modules can hook inside your query, can support multiple DB drivers
Cons: Object initialization is slow, lots of DB connection/traffic

CASE 2: It's faster if you use db_query()...

... because in that case there's no object instantiation/operation, which is a bit costly and other conversion. (i.e.: 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)
  );
}

Pros: no object initialization and operation < so it's faster
Cons: other modules can't hook inside your query, your code can break under different DB drivers, lots of DB connection/traffic

CASE 3: Also you can use transaction

This can boost a bit your performance at some case.

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

Note: transaction mostly used, when you want everything or nothing. But with some DB drivers, you can optimize out the COMMIT commands.
In case 1 you get something like this:

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;

With transaction you do something like this:

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;

With COMMIT you write out the data to the final place (into long-term storage, until this, it's only somewhere in the memory or a temporary place). When you use rollback, you drop these changes. At least this is how I understand this. (i.e. I get performance improvement with this when I used sqlite.)

Same as case 1 or 2 +
Pros: you can rollback your data if you need to be consistent, you write out data only once to the drive < so sql only "once" do IO, lots of DB connection/traffic

CASE 4: Or you can build one sql statement for this:

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

Probably this is the fastest way from all from here.
NOTE: $when_then variable doesn't contain the best solution, it's better if you can use the params or you escape unsafe data.

Pros: between your server and sql there will be only one request and less redundant data, one bigger sql queries faster then a lot of small ones
Cons: other modules can't hook inside your query, your code can break under different DB drivers

Also please note, after (I think) PHP 5.3 you can't run more than one statement in db_query or in PDO by default, because of it can be an SQL Injection, so it blocks. However you can set this, but not recommended. (PDO support for multiple queries (PDO_MYSQL, PDO_MYSQLND))

13-05-2019 edit:
Just a side note, I did a few month ago some performance measurement on medium dataset to query down some data via db_select and db_query. The magnitude of the performance of these are: under you run one db_select, you can run two db_query. This also applies to db_update and db_query. The test queried down some columns with a simple SELECT, no JOINS, no fancy dandy stuff, just two condition. Of course, the measurement also contained/included the time which was required to build up these queries (at both case). However please note, the Drupal Coding Standard requires to use db_update, db_delete, db_merge on modification normally, only 'allows' db_query instead of db_select operation.




回答2:


Using BULK UPDATE you can update multiple values on diferent rows or in the same row with the dinamic query db_update .

Note: By bulk updating only one query can be sent to the server instead of one query for each row to update.

Syntax:

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

More info about expressions on UpdateQuery on: https://api.drupal.org/api/drupal/includes%21database%21query.inc/function/UpdateQuery%3A%3Aexpression/7.x

With your data will be:

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

As result the next query is generated:

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:


I think you could do something like

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

Where $fields = array ('field_1' => VALUE, 'field_2' => VALUE)



来源:https://stackoverflow.com/questions/25646525/drupal-7-update-multiple-rows-using-db-update

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!