Drupal database API query - row.update if exists, else row.insert
I've been trying to run a query in drupal that'll update entries if they already exists or insert a new entry if it doesnt. The code looks like this at the moment:
db_query("IF EXISTS (SELECT %d FROM {uc_posten_packages.pid})
UPDATE {uc_posten_packages} SET title = '%s', label = '%s', cost = '%d', length = '%d', width ='%d', height = '%d', weight = '%d' WHERE pid = %d
ELSE
INSERT INTO {uc_posten_packages} VALUES ('%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d')",$id, $title, $label, $rate, $length, $width, $height, $weight, $id, $id, $title, $label, $rate, $length, $width, $height, $weight);
I can't see why that query throws me an error. All the numbers in the error are correct
...near 'IF EXISTS (SELECT 1 FROM uc_posten_packages.pid) UPDATE uc_posten开发者_开发技巧_packages ' at line 1 query:
IF EXISTS (SELECT 1 FROM uc_posten_packages.pid) UPDATE uc_posten_packages SET title = 'vfbv', label = 'bbv', cost = '22', length = '232', width ='22', height = '22', weight = '22' WHERE pid = 1 ELSE INSERT INTO uc_posten_packages VALUES ('1', 'vfbv', 'bbv', '22', '232', '22', '22', '22')
Should this query work and/or is there some better way dealing with this in drupal?
What you're looking for is cordially called an "upsert" (update otherwise insert), and there is a drupal function for just this: db_merge. Good write-up here: http://drupal.org/node/310085
drupal_write_record() does not automatically perform an "upsert." It always either inserts or updates based on what you pass in only, but not both.
[UPDATE Jan 2013]
This answer refers to an older version of Drupal than the current stable release. Please edit this answer by adding an update section like this with more up-to-date information, as I no longer work with Drupal or keep up with its API changes.
-semperos
[/UPDATE Jan 2013]
You have a couple of options, namely drupal_write_record
or running a sample query and testing for results. This shouldn't be done in a single SQL query as in your question.
drupal_write_record (Preferred Method)
The function drupal_write_record
allows you to specify the table you want to deal with and an object (or associate array) which contains data for each column/field of that table. If you already have the primary key for the row you're trying to update, then you include it as a third parameter to the function, and drupal_write_record
will automatically use the SQL UPDATE
command. Otherwise, it defaults to INSERT
. For example:
drupal_write_record('uc_posten_packages', array('title' => "Foobar",
'label' => "foobar",
'cost' => 10,
'length' => 100,));
This will INSERT
a new record with that information. If you had included a third argument of array($pid)
where $pid
was the name of the field that acts as primary key for the table, it would have performed an update instead.
This function will only work if the table you're dealing with was defined using hook_schema
, which should be true of any properly developed Drupal module that has database tables. Since this function uses the schema to ensure it's writing to the database correctly, you should use this function when possible (or when no other more specific functions exist, e.g. node_save
for node objects).
Test Query
You can just run a sample query using db_result(db_query("SELECT..."))
. It returns an empty string if no results are found, which evaluates to false in PHP, so your code could look like:
if (db_result(db_query("SELECT * FROM {uc_posten_packages} WHERE pid = %d", $pid))) {
// UPDATE
} else {
// INSERT
}
According to drupal_write_record the operation was an UPDATE
or an INSERT
you need to know whether the record existed or not before committing the transaction. This is not what drupal_write_record() does. This function just checks it's arguments and issues an update or an insert accordingly. So there is really no way to tell if the function did an INSERT
or an UPDATE
.
The solution is using db_merge() query, it is a relatively new SQL ANSI standard that does exactly what I needed (and in the case of MySQL does it in an atomic transaction).
$return_value = db_merge('mytable')
->key(array('id' => $key))
->fields($fields)
->updateFields(array(
'updatedon' => $now,
))
->execute();
The return value is an integer returning 1 for inserts and 2 for updates.
This is a silly example of replace syntax.
mysql> create table test (
-> id int,
-> a int,
-> b int,
-> unique (id)
-> ) engine = myisam;
Query OK, 0 rows affected (0.01 sec)
mysql> replace into test (id,a,b) values (1,4,2);
Query OK, 1 row affected (0.00 sec)
mysql> replace into test (id,a,b) values (2,10,3);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+------+------+------+
| id | a | b |
+------+------+------+
| 1 | 4 | 2 |
| 2 | 10 | 3 |
+------+------+------+
2 rows in set (0.00 sec)
mysql> replace into test (id,a,b) values (1,5,5);
Query OK, 2 rows affected (0.00 sec)
mysql> select * from test;
+------+------+------+
| id | a | b |
+------+------+------+
| 1 | 5 | 5 |
| 2 | 10 | 3 |
+------+------+------+
2 rows in set (0.00 sec)
Hope it could help you.
精彩评论