19

I have an array full of random content item ids. I need to run a mysql query (id in the array goes in the WHERE clause), using each ID that's in the array, in the order that they appear in the said array. How would I do this?

This will be an UPDATE query, for each individual ID in the array.

5 Answers5

39

As with nearly all "How do I do SQL from within PHP" questions - You really should use prepared statements. It's not that hard:

$ids  = array(2, 4, 6, 8);

// prepare an SQL statement with a single parameter placeholder
$sql  = "UPDATE MyTable SET LastUpdated = GETDATE() WHERE id = ?";
$stmt = $mysqli->prepare($sql);

// bind a different value to the placeholder with each execution
for ($i = 0; $i < count($ids); $i++)
{
    $stmt->bind_param("i", $ids[$i]);
    $stmt->execute();
    echo "Updated record ID: $id\n";
}

// done
$stmt->close();

Alternatively, you can do it like this:

$ids    = array(2, 4, 6, 8);

// prepare an SQL statement with multiple parameter placeholders
$params = implode(",", array_fill(0, count($ids), "?"));
$sql    = "UPDATE MyTable SET LastUpdated = GETDATE() WHERE id IN ($params)";
$stmt   = $mysqli->prepare($sql);

// dynamic call of mysqli_stmt::bind_param                    hard-coded eqivalent
$types = str_repeat("i", count($ids));                        // "iiii"
$args = array_merge(array($types), $ids);                     // ["iiii", 2, 4, 6, 8]
call_user_func_array(array($stmt, 'bind_param'), ref($args)); // $stmt->bind_param("iiii", 2, 4, 6, 8)

// execute the query for all input values in one step
$stmt->execute();

// done
$stmt->close();
echo "Updated record IDs: " . implode("," $ids) ."\n";

// ----------------------------------------------------------------------------------
// helper function to turn an array of values into an array of value references
// necessary because mysqli_stmt::bind_param needs value refereces for no good reason
function ref($arr) {
    $refs = array();
    foreach ($arr as $key => $val) $refs[$key] = &$arr[$key];
    return $refs;
}

Add more parameter placeholders for other fields as you need them.

Which one to pick?

  • The first variant works with a variable number of records iteratively, hitting the database multiple times. This is most useful for UPDATE and INSERT operations.

  • The second variant works with a variable number of records too, but it hits the database only once. This is much more efficient than the iterative approach, obviously you can only do the same thing to all affected records. This is most useful for SELECT and DELETE operations, or when you want to UPDATE multiple records with the same data.

Why prepared statements?

  • Prepared statements are a lot safer because they make SQL injection attacks impossible. This is the primary reason to use prepared statements, even if it is more work to write them. A sensible habit to get into is: Always use prepared statements, even if you think it's "not really necessary." Neglect will come and bite you (or your customers).
  • Re-using the same prepared statement multiple times with different parameter values is more efficient than sending multiple full SQL strings to the database, because the database only needs to compile the statement once and can re-use it as well.
  • Only parameter values are sent to the database on execute(), so less data needs to go over the wire when used repeatedly.

In longer loops the execution time difference between using a prepared statement and sending plain SQL will become noticeable.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 1
    Thats the strangest php syntax Ive ever seen. Had to loop up a bunch of those functions. –  Dec 01 '08 at 09:03
  • In the second method , where do we specify the data type for all the values of array? – jackkorbin Mar 17 '17 at 13:40
  • 1
    The above solution did not work for me. http://www.pontikis.net/blog/dynamically-bind_param-array-mysqli <-- This worked like a charm. – jackkorbin Mar 17 '17 at 14:11
  • @jackkorbin 27 people think it works just fine, you think it does not work. I think that means you made a mistake. – Tomalak Mar 17 '17 at 16:00
  • Well i tried it all ways , it did not work. There is a possibility that Your code might became outdated as it is from 2008. Moreover the 2nd method does not bind the type of variables from array. – jackkorbin Mar 17 '17 at 20:03
  • Could you supply a link to pastebin (or equivalent) so I can have a look at your code and if necessary update my answer? – Tomalak Mar 17 '17 at 21:16
  • @Tomalak shouldn't `'bindparams'` be `'bind_param'`? I think this is what jackkorbin was getting at. (after I compared yours with his link) Please confirm what is correct. Thanks. – mickmackusa Dec 07 '17 at 13:01
  • @mickmack I think you are right - the function is named `bind_param`. I've updated my code. – Tomalak Dec 07 '17 at 14:20
  • and where is the `s = string, i = integer, d = double, b = blob` part? and the variables by reference? This doesn't seem complete to me. You may have misled 27 people (or maybe everyone is just using the first code block) – mickmackusa Dec 07 '17 at 14:22
  • 3
    The answer is from 2008. Can it be that PHP has substantially changed its mysqli API since then? – Tomalak Dec 07 '17 at 14:29
  • I don't have any reason to believe that this is a php version issue. Your 2nd solution really seems incomplete to me. Because this answer has a green tick and so many upvotes, I would urge you to prioritize fixing this up. – mickmackusa Dec 07 '17 at 14:33
  • @mickmack I think the issues are addressed now. Wrote a better blurb of text, too, – Tomalak Dec 07 '17 at 15:51
  • I didn't have a chance to actually test. Did you test? This other answer seems to insist that passing by reference is required. https://stackoverflow.com/a/24713481/2943403 (and I have read this remark in a few other posts around the web.) I am still researching this topic for my own benefit. Have a look at this too: https://stackoverflow.com/a/37047265/2943403 – mickmackusa Dec 07 '17 at 15:58
  • 1
    You can eliminate the need for the `ref()` function by using the splat operator (introduced in PHP 5.6) `$stmt->bind_param(...$args)` – M. Salman Khan Sep 29 '19 at 11:11
2

Using the "IN" Clause

Might be what you're after

$ids = array(2,4,6,8);
$ids = implode($ids);
$sql="SELECT * FROM my_table WHERE id IN($ids);";
mysql_query($sql);

otherwise, what's wrong with

$ids = array(2,4,6,8);
foreach($ids as $id) {
    $sql="SELECT * FROM my_table WHERE ID = $id;";
    mysql_query($sql);
}
Dean Rather
  • 31,756
  • 15
  • 66
  • 72
1

Amen to Tomalak's comment on statements.

However, if you do not wish to use mysqli, you can always use intval() to prevent injection:

$ids  = array(2, 4, 6, 8);
for ($i = 0; $i < count($ids); $i++)
{
    mysql_query("UPDATE MyTable SET LastUpdated = GETDATE() WHERE id = " . intval($ids[$i]));
}
Fusion
  • 374
  • 5
  • 12
1
$values_filtered = array_filter('is_int', $values);
if (count($values_filtered) == count($values)) {
    $sql = 'update table set attrib = 'something' where someid in (' . implode(',', $values_filtered) . ');';
    //execute
} else {
    //do something
}
OIS
  • 9,833
  • 3
  • 32
  • 41
0

You could do something like the following, however you need to be VERY careful that the array only contains integers otherwise you could end up with SQL injection.

You really don't want to be doing multiple queries to get the content out if you can help it. Something like this might be what you are after.

foreach ($array as $key = $var) {
   if ((int) $var <= 0) {
       unset($array[$key]);
   }
}


$query = "SELECT * 
from content 
WHERE contentid IN ('".implode("','", $array)."')";

$result = mysql_query($query);
Rodney Amato
  • 1,238
  • 9
  • 10
  • Its an admin-only function, so nobody except myself would be executing it. I need to run an UPDATE query for every ID in the array. –  Dec 01 '08 at 08:27