7

I was wondering if it is possible to use $wpdb->update to update values for multiple rows like with IN in "normal" SQL.

I want to accomplish something like this example;

UPDATE [table]
SET [column_1] = [updated_value]
WHERE [column_2] IN ([comma_separated_ids])

I've been searching around for how this would work, but I'm yet to find another question asking this or any kind of answer/blog post explaining whether it can be done or not.

For now I'm using a query instead, but it would be nicer with a simple one liner.

3
  • 1
    There is no reason for it not to work. Commented Aug 1, 2014 at 14:18
  • 1
    But HOW? I tried adding a comma separated string as value in the where array, but it only updated the first id. Commented Aug 1, 2014 at 14:54
  • hmm, I see that people with better knowledge then me answered it ;) Commented Aug 1, 2014 at 15:41

1 Answer 1

12

As you can see in source code the = sign is hardcoded in the wpdb::update() method, so, by default, is not possible to use IN for update method.

Simplest way to do the trick is to use the wpdb::query() with your sql query, just be sure to properly escape all values

Example:

function wpdb_update_in( $table, $data, $where, $format = NULL, $where_format = NULL ) {

    global $wpdb;

    $table = esc_sql( $table );

    if( ! is_string( $table ) || ! isset( $wpdb->$table ) ) {
        return FALSE;
    }

    $i          = 0;
    $q          = "UPDATE " . $wpdb->$table . " SET ";
    $format     = array_values( (array) $format );
    $escaped    = array();

    foreach( (array) $data as $key => $value ) {
        $f         = isset( $format[$i] ) && in_array( $format[$i], array( '%s', '%d' ), TRUE ) ? $format[$i] : '%s';
        $escaped[] = esc_sql( $key ) . " = " . $wpdb->prepare( $f, $value );
        $i++;
    }

    $q         .= implode( $escaped, ', ' );
    $where      = (array) $where;
    $where_keys = array_keys( $where );
    $where_val  = (array) array_shift( $where );
    $q         .= " WHERE " . esc_sql( array_shift( $where_keys ) ) . ' IN (';

    if( ! in_array( $where_format, array('%s', '%d'), TRUE ) ) {
        $where_format = '%s';
    }

    $escaped = array();

    foreach( $where_val as $val ) {
        $escaped[] = $wpdb->prepare( $where_format, $val );
    }

    $q .= implode( $escaped, ', ' ) . ')';

    return $wpdb->query( $q );
}

Then use it like so:

wpdb_update_in(
  'posts', // table
  array( 'post_author' => '1', 'post_status' => 'draft' ), // data
  array( 'post_author' => array( '2', '3', '4', '5' ) ), // where
  array( '%d', '%s' ), // format
  '%d' // where format
);

The SQL performed will be

UPDATE wp_posts
SET post_author = 1, post_status = 'draft'
WHERE post_author IN (2, 3, 4, 5)
5
  • I haven't worked that much in WP, so I was unaware of the source code. I'll make sure to bookmark it for future reference. I like your solution and I'll mark as the answer. Commented Aug 4, 2014 at 7:13
  • Hard coded wordpress and hard to be used... Commented Sep 10, 2017 at 22:00
  • 1
    I found this useful, but it looks like some things have changed since this answer was made. First, the signature for implode() has switched the parameter order from PHP 7.4. Also, I couldn't figure out what $wpdb->$table was? Removed this in the end.
    – JDandChips
    Commented Jun 16, 2021 at 14:21
  • there's also %f format to authorize Commented Apr 20 at 21:55
  • Things have changed in 10 years, yes :) $wpdb->$table is the way you get the full table name. You should not use, for example, "wp_content" but $wpdb->content, because the prefix might change, and in multisite will change. This has not changed. But today we have the %i placeholder to escape table names. Regarding %f, yes that was added relatively recently alongside %i mentioned above.
    – gmazzap
    Commented May 2 at 18:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.