tags:

views:

182

answers:

6

Hello. I would like to update multiple records in a MySQL table using a single query. Basically, this is a tasks table, which has assignments for different people on different dates. When these assignments are changed and submitted via the Online form there is a lot of POST data that gets submitted (pretty much all the pending assignments). I've written an algorithm that sorts through all this information and gets what I want out of it, but I'm stuck on writing the query to update the MySQL table:

  // Find the modified records and save their information
  $update = 0;
  for ( $n = 0; $n < $total_records; $n++ )
  {
     if ( $_POST['update'.$n] == true )
     {
        $updates_arr[$update] = array( intval($_POST['user_id'.$n]), intval($_POST['task'.$n]), $_POST['date'.$n] );
        $update++;
     }
  }

  if ( $mysql_db = OpenDatabase() )
  {
     $query  = "UPDATE tasks_tbl";
     if ( $updates_arr[0] )
     {
        $query .= "   SET task = ".$updates_arr[0][1]." WHERE user_id = ".$updates_arr[0][0]." AND date = ".$updates_arr[0][2];
     }

     for ( $n = 1; $n < $updates; $n++ )
     {
        $query .= ",   SET task = ".$updates_arr[$n][1]." WHERE user_id = ".$updates_arr[$n][0]." AND date = ".$updates_arr[$n][2];
     }

     $result = mysql_query( $query, $mysql_db );

     if ( $result )
     {
        $page .= "<p>Success!</p>\n\n";
     }
     else
     {
        $page .= "<p>Error: ".mysql_error()."</p>\n\n";
     }
  }

This is the query that is generated:

UPDATE tasks_tbl 
   SET task = 1 
 WHERE user_id = 16 
   AND date = 2010-05-05,  
   SET task = 1 
 WHERE user_id = 17 
   AND date = 2222-02-22

Any suggestions would be appreciated. Thanks.

A: 

I don't think this is possible with one statement. You will need to create separate UPDATE statements:

UPDATE tasks_tbl SET task = 1 WHERE user_id = 16 AND date = 2010-05-05;
UPDATE tasks_tbl SET task = 1 WHERE user_id = 17 AND date = 2222-02-22

You can pass them into mysql_query() as one string separated by ';' if you set mysql to accept multiple queries:

Multiple queries seem to be supported. You just have to pass flag 65536 as mysql_connect's 5 parameter (client_flags)

Matt McCormick
First complete sentence in the documentation:mysql_query() sends a unique query (**multiple queries are not supported**) to the currently active database on the server that's associated with the specified link_identifier .
BipedalShark
I didn't realise the only update would be changing the task to 1. I think it'd be easier to just issue a new statement for each UPDATE.
Matt McCormick
It is not possible because he clarified that some tasks will be set to 1, 2 or 3
Matt McCormick
It's still quite possible to do in one query. It might not be a good idea, but it's possible.
Tom H.
I would be interested in seeing the MYSQL code for that Tom. I'm unaware that I can issue conditional SETs with one UPDATE statement.
Matt McCormick
+5  A: 

You can generate a query like this:

UPDATE tasks_tbl SET task=1 WHERE 
    (user_id=16 AND date='2010-05-05') OR
    (user_id=17 AND date='2010-02-22')

There are hacks to avoid using (... and ...) or (... and ...) constructs (concatenate fields and params: "concat(user_id, date) = '". $user_id. $date. "'", but they work a bit slower.

The PHP code:

for ($i = 0; !empty($_POST['update'. $i]; $i++)
    if (intval($_POST['task'.$i]) == 1)
        $cond[] = '(user_id='. intval($_POST['user_id'. $i]).
        ' and date=\''. mysql_real_escape_string($_POST['date'.$i]). '\')';

$query = 'UPDATE tasks_tbl SET task=1 WHERE '. implode(' OR ', $cond). ')';

Edit: I don't quite understand why you need to do that in a single query. How many values task can have? 1, 2, 3, or many more? With 3 values, you can use nested IF(...) functions:

UPDATE tasks_tbl SET task=if('. <imploded tasks with value 1>. ', 1, if('.
<tasks with value 2>. ', 2, if('. <tasks with 3>. ', 3,
task))) /* leave as is otherwise */

Or you may put a simple loop on the code I've given:

for ($j = 1; $j <= 3; $j++)
    for ($i = 0; !empty($_POST['update'. $i]; $i++)
        if (intval($_POST['task'.$i]) == 1)
            $cond[] = '(user_id='. intval($_POST['user_id'. $i]).
            ' and date=\''. mysql_real_escape_string($_POST['date'.$i]). '\')';

    mysql_query('UPDATE tasks_tbl SET task=1 WHERE '. implode(' OR ', $cond). ')');
culebrón
Assuming task is always set to 1..
Stuart Dunkeld
Ooops! Thanks, fixed that :)
culebrón
A: 

Are you looking for this:

UPDATE tasks_tbl 
   SET task = 1 
 WHERE (user_id = 16 AND date = 2010-05-05) 
       OR (user_id = 17 AND date = 2222-02-22)

Or you are trying to set 'task' to different values in different rows with a single statement? The latter is just not possible

mfeingold
culebrón
+1  A: 

I would prefer to use a prepared query and loop over the data (inside a transaction if needed). That makes it simpler to understand, which is better for maintainability.

Your code smells of sql injection insecurity, too, which prepared queries would eliminate.

See: http://www.php.net/manual/en/mysqli.prepare.php or even better with PDO prepare:

DGM
Thanks, but this looks like it would require a drastic re-write of all the PHP -> MySQL code on my website. Right now, I'm considering switching from mysql_connect() to mysql_pconnect(), which should only require a few changes. I'll definitely keep the mysqli APIs in mind for my next site, though!
Jim Fell
There is a lot more code than what I have shown. I do use the mysql_real_escape_string() and validate the results to be within expected ranges, so it would be pretty difficult, if not impossible, to inject anything into the query.
Jim Fell
+2  A: 

I disagree with your architecture here, but the following should work. Use at your own risk:

UPDATE
     Tasks_Table
SET
     task =
          CASE
               WHEN user_id = 16 AND date = 2010-05-05 THEN 1
               WHEN user_id = 17 AND date = 2222-02-22 THEN 1
               ...
          END
WHERE
     (user_id = 16 AND date = 2010-05-05) OR
     (user_id = 17 AND date = 2222-02-22) OR
     ...

In your example you have task = 1 in all cases, but with the CASE statement you can change them to be what you need for each case. I'll leave the string building to you.

Tom H.
Thanks Tom. I see how this works although it would be ugly and a pain to ever debug.
Matt McCormick
This does look interesting. Are CASE statements commonly used, or is this a more obscure feature of MySQL?
Jim Fell
Actually, I'm a MS SQL developer for the most part. The CASE statement is a part of the SQL ANSI standard, so it should be available in most RDBMSs
Tom H.
A: 

Thanks for the suggestions, everyone. I ended up going with the multiple queries, as it apparently was not going to be as simple to do, as I had hoped.

    foreach ( $updates_arr as $record => $data ):
       $query  = "UPDATE tasks_tbl";
       $query .= "   SET task = ".$data[1];
       $query .= "   WHERE task_id = ".$data[0];
       $result = mysql_query( $query, $mysql_db );
       if ( !$result )
       {
          break;
       }
       endforeach;
Jim Fell
Again, If you would use prepared statements, you wouldn't have to rebuild the query string every time through the loop, you wouldn't send the entire query over the network every time, the sql server would not have to parse the sql every time, and you would be protected from SQL injection, which your code is not.
DGM