Showing posts with label SQL. Show all posts
Showing posts with label SQL. Show all posts

Thursday, 21 November 2013

SQL update multiple rows with a single query, different value per row

Problem:

I'd like to update many rows with each row having a possibly different value, while using only a single SQL query. A query with a single WHERE clause would be inadequate as a result.

Warning:

This how-to can possibly ruin a database if done wrong, so make sure that you first test it using toy data (and make sure it works properly). Use at your own risk!

Please feel free to point out any typos and mistakes in the comments below.

Solution:

This is a summary (for myself) of the solution found at Karl Rixon's page here. For greater detail, please follow the aforementioned link.

Example table:

We'll use the following table for this example:

Table name: 'membership_list'
IDS_IDNameCountryOne_or_zero
1205G0BobUSA1
2205G0CatUK0
3319E8JaneAustralia1
4518A1JoeIceland1
...............

Example 1 - multiple rows, single column:

In order to make multiple updates, you can use a CASE block in SQL combined with an appropriate WHERE clause to select the appropriate rows and set the different values. For example, in order to update the column `Country` based on column `ID` alone:

UPDATE `membership_list`
  SET `Country` = CASE `ID`
    WHEN '1' THEN 'Antarctica'
    WHEN '3' THEN 'Canada'
  END
WHERE `ID` IN (1,3);

Do NOT forget the WHERE clause otherwise all other values will be set to NULL.

Example 2 - multiple rows, multiple columns:

In order to change more than one column, more than one CASE blocks can be used. For example, in order to both the columns `Country` and `One_or_zero` based on `ID` alone:

UPDATE `membership_list`
  SET `Country` = CASE `ID`
    WHEN '1' THEN 'Antarctica'
    WHEN '3' THEN 'Canada'
  END,
      `One_or_zero` = CASE `ID`
    WHEN '1' THEN '1'
    WHEN '3' THEN '1'
  END
WHERE `ID` IN (1,3);

Example 3 - multiple rows, multiple WHERE conditions:

Now assuming that you only want to alter all rows in `membership_list` that have '205G0' as the `S_ID` and 'Bob' as the `Name`, and we want to set all `Country` to unique values based on `One_or_zero`:

UPDATE `membership_list`
  SET `Country` = CASE `One_or_zero`
    WHEN '1' THEN 'Switzerland'
    WHEN '0' THEN 'Russia'
  END
WHERE `One_or_zero` in (0,1) 
  AND `S_ID`='205G0'
  AND `Name`='Bob';

Example 4 - constructing a PHP query for Example 1:

Among other benefits, a single query for multiple updates becomes particularly useful when using server-side scripting such as PHP, as there is a significant gain of speed when compared to looping through many single queries.

Here is an example script to construct the query for the first example above:

$arr_id_country = array(
    '1' => 'Antarctica',
    '3' => 'Canada'
  );

$str_ids = implode(',', array_keys($arr_id_country));

$str_when_then = "";
foreach($arr_id_country as $id => $country) {
  $str_when_then .= sprintf(" WHEN '%d' THEN '%s' ",
      $id,
      $country // note, you'd sanitize this if from user input
  );
}

// whitespace + appends included in example for readability
$template =   "UPDATE `membership_list` "
            . "   SET `Country` = CASE `ID` "
            . "     %s "
            . "   END "
            . " WHERE `ID` IN (%s);"

$query = sprintf($template, $str_when_then, $str_ids);

// do database queries, etc., here ...

Notes:

Once again, do NOT forget to include the WHERE clause that contains all the values found in the CASE block in order to prevent accidentally affecting other rows unintentionally.

References:

Saturday, 14 April 2012

Native SQL with Doctrine2 Fixtures in Symfony2

Sometimes the use of native SQL queries is desired over using Doctrine2 when programming fixtures in Symfony2, whether this be for performance reasons, etc. While it's recommended to use Doctrine2 in most cases, here's how to execute a SQL query:

$manager->exec($yoursql);

Note that this assumes that $manager is an ObjectManager type (hint: it should already be if you're creating your own fixture that extends Doctrine\Common\DataFixtures\AbstractFixture).

Doctrine fixtures is still changing, so I'll note that this works as of April 14, 2012 but it might not if they change their public interface in the future.