I have several hundred records that I need to save into a database table. Since some of them already exist and must be updated, I use db_merge() instead of db_insert().

However, when trying to use the db_merge() query builder I get a fatal error:

Fatal error: Call to undefined method MergeQuery_mysql::values()

The documentation suggests using values() when adding multiple rows, since fields() may only be called a single time.

What am I doing wrong here?

Comments

damien tournoud’s picture

You are not doing anything wrong: db_merge() only supports one row queries.

cburschka’s picture

I see that this may not be supported currently. However, I know MySQL supports doing this in a single query. The base type MergeQuery already implements a degenerate case with multiple queries, surely adding another such case that loops over the rows would not be so bad? MySQL and other drivers that support single query multiple-record merging could then extend this.

To show the problem:

  // Should work with multiple-record merge support:
  $query = db_merge('topic');
  $query->key(array('post_id')); // (needs a better API)
  
  foreach ($topics as $topic) {
    $query->values(array(
      'post_id'  => $topic->post_id,
      'forum_id' => $forum_id,
      'author'   => $topic->author,
      'lastpost' => $topic->lastpost,
      'length'   => $topic->length,
      'title'    => $topic->title,
    ));
  }
  $query->execute();
  
  // This requires a separate query for each record, as it is now.
  foreach ($topics as $topic) {
    db_merge('topic')
      ->key(array('post_id' => $topic->post_id))
      ->fields(array(
      'post_id'  => $topic->post_id,
      'forum_id' => $forum_id,
      'author'   => $topic->author,
      'lastpost' => $topic->lastpost,
      'length'   => $topic->length,
      'title'    => $topic->title,
    ))
      ->execute();
  }

damien tournoud’s picture

In that case ->key() should also accept multiple values.

Crell’s picture

Just how does MySQL handle multi-value insert AND merge in the same query? Is it even valid syntax? The whole reason we split out Merge queries as a separate entity is that there was no sane way I could find to fold them into insert queries without "if you call this method you can't call this other method" silliness.

cburschka’s picture

Might be MySQL 5.0 specific. Not sure if we require that version already.

http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html

The ON DUPLICATE KEY UPDATE clause can contain multiple column assignments, separated by commas.

You can use the VALUES(col_name) function in the UPDATE clause to refer to column values from the INSERT portion of the INSERT ... UPDATE statement. In other words, VALUES(col_name) in the UPDATE clause refers to the value of col_name that would be inserted, had no duplicate-key conflict occurred. This function is especially useful in multiple-row inserts. The VALUES() function is meaningful only in INSERT ... UPDATE statements and returns NULL otherwise. Example:

INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

That statement is identical to the following two statements:

INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=3;
INSERT INTO table (a,b,c) VALUES (4,5,6)
ON DUPLICATE KEY UPDATE c=9;

Crell’s picture

hm. I'm not sure that's flexible enough for our needs. It looks like you can then only use expressions in the update portion, which is very limiting.

My gut feeling here is that this is not going to be possible with anything resembling a good generic syntax in the query builder. If you can come up with one, though, and an implementation for both MySQL and generic (Postgres and SQLite can come up with their own implementations if appropriate), then I'm OK with it.

Crell’s picture

Title: db_merge()->values() doesn't work. » Multi-value merge queries

Changing title.

Crell’s picture

Version: 7.x-dev » 8.x-dev
damien tournoud’s picture

Status: Active » Closed (won't fix)

Given the new merge queries, this is won't fix.

j0rd’s picture

I need multi-value merge as well. I have a couple thousand rows which need to get "merged". This action happen fairly often in my website, so performing a query per row is not an option.

Probably the easiest way to implement this, would be to split it up into max 3 queries. This would be very flexible, and you could continue to use the query architecture Drupal has.

1 multi-merge would be 1 select, some PHP, then 1 multi-insert and 1 multi-update.

juanjo_vlc’s picture

In several threads about merge querys I never read anyone talking about mysql replace syntax.
It allows replacement of multiple rows at once, but only works for full row updates.
http://dev.mysql.com/doc/refman/5.0/en/replace.html and there is not a "db_replace" function on drupal's database abstraction.