Запрос API базы данных Drupal — строка.обновить, если существует, иначе строка.вставить

#mysql #drupal #upsert

#mysql #drupal #ввод

Вопрос:

Я пытался запустить запрос в drupal, который обновит записи, если они уже существуют, или вставит новую запись, если этого не происходит. На данный момент код выглядит следующим образом:

 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);
  

Я не понимаю, почему этот запрос выдает мне ошибку. Все цифры в ошибке верны

 ...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')
  

Должен ли этот запрос работать и / или есть какой-то лучший способ справиться с этим в drupal?

Комментарии:

1. Вы не можете сделать ничего подобного. Взгляните на синтаксис замены. dev.mysql.com/doc/refman/5.0/en/replace.html

Ответ №1:

То, что вы ищете, обычно называется «upsert» (обновление, иначе вставка), и для этого есть функция drupal: db_merge. Хорошая статья здесь:http://drupal.org/node/310085

drupal_write_record() автоматически не выполняет «upsert». Он всегда либо вставляет, либо обновляет только на основе того, что вы передаете, но не на обоих.

Ответ №2:

[ОБНОВЛЕНИЕ январь 2013]

Этот ответ относится к более старой версии Drupal, чем текущая стабильная версия. Пожалуйста, отредактируйте этот ответ, добавив раздел обновления, подобный этому, с более актуальной информацией, поскольку я больше не работаю с Drupal и не слежу за изменениями в его API.

-semperos

[/ ОБНОВЛЕНИЕ от января 2013]

У вас есть несколько вариантов, а именно drupal_write_record или запуск образца запроса и тестирование результатов. Это не должно выполняться в одном SQL-запросе, как в вашем вопросе.

drupal_write_record (предпочтительный метод)

Функция drupal_write_record позволяет вам указать таблицу, с которой вы хотите иметь дело, и объект (или ассоциированный массив), который содержит данные для каждого столбца / поля этой таблицы. Если у вас уже есть первичный ключ для строки, которую вы пытаетесь обновить, тогда вы включаете его в качестве третьего параметра в функцию, и drupal_write_record автоматически будет использоваться команда SQL UPDATE . В противном случае значение по умолчанию равно INSERT . Например:

 drupal_write_record('uc_posten_packages', array('title'  => "Foobar",
                                      'label'  => "foobar",
                                      'cost'   => 10,
                                      'length' => 100,));
  

Это INSERT создаст новую запись с этой информацией. Если бы вы включили третий аргумент, array($pid) где $pid было имя поля, которое действует как первичный ключ для таблицы, вместо этого было бы выполнено обновление.

Эта функция будет работать, только если таблица, с которой вы имеете дело, была определена с помощью hook_schema , что должно быть верно для любого правильно разработанного модуля Drupal, который имеет таблицы базы данных. Поскольку эта функция использует схему для обеспечения правильной записи в базу данных, вы должны использовать эту функцию, когда это возможно (или когда не существует других более специфичных функций, например node_save для объектов узла).

Тестовый запрос

Вы можете просто запустить пример запроса с помощью db_result(db_query("SELECT...")) . Он возвращает пустую строку, если результаты не найдены, которая в PHP оценивается как false, поэтому ваш код может выглядеть следующим образом:

 if (db_result(db_query("SELECT * FROM {uc_posten_packages} WHERE pid = %d", $pid))) {
  // UPDATE
} else {
  // INSERT
}
  

Комментарии:

1. Спасибо за ответ! Это великолепно, но я не могу заставить его обновиться. Я добавил массив ($ pid) в качестве третьего аргумента, но он выдает мне ошибку: неподдерживаемый тип для db_type_placeholder. Он работает со вставками, хотя, точно такой же запрос, но без третьего аргумента.

2. @Toxid Я обновил свой ответ, чтобы быть более точным. Если вы посмотрите на api.drupal.org документы для drupal_write_record, третьим необязательным аргументом должно быть имя поля, которое действует как первичный ключ для таблицы, с которой вы работаете, а не фактическое значение первичного ключа. Смотрите код функции node_save в качестве примера обоих типов drupal_write_record (вставка и обновление) здесь: api.drupal.org/api/drupal/modules—node—node.module/function /…

3. Ах, красиво! Я изучал API, но я был настолько уверен, что это значение первичного ключа, которое он искал, я забыл о возможности того, что это может быть что-то другое. Спасибо!

4. Глядя на исходный код drupal_write_record() я не вижу логики в принятии решения о выполнении запроса INSERT или UPDATE на основе того, что уже есть в БД. Вместо этого я вижу, что это зависит от входных параметров (параметр $ update). Поэтому я бы исправил ответ, удалив всю часть drupal_write_record().

5. Я больше не работаю на PHP или Drupal. Если кто-то хочет отредактировать мой ответ с информацией, относящейся к последней стабильной версии Drupal, я бы приветствовал редактирование. Тем временем я отредактирую ответ, чтобы уточнить, что этот ответ устарел.

Ответ №3:

Согласно drupal_write_record, операция была UPDATE или INSERT вам нужно знать, существовала запись или нет, прежде чем совершать транзакцию. Это не то, что делает drupal_write_record(). Эта функция просто проверяет свои аргументы и выдает обновление или вставку соответственно. Таким образом, на самом деле нет способа определить, выполнила ли функция INSERT или an UPDATE .

Решение заключается в использовании запроса db_merge(), это относительно новый стандарт SQL ANSI, который делает именно то, что мне было нужно (и в случае MySQL делает это в атомной транзакции).

 $return_value = db_merge('mytable')
            ->key(array('id' => $key))
            ->fields($fields)
            ->updateFields(array(
                'updatedon' => $now,
            ))
            ->execute();
  

Возвращаемое значение представляет собой целое число, возвращающее 1 для вставок и 2 для обновлений.

Ответ №4:

Это глупый пример синтаксиса замены.

 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)
  

Надеюсь, это могло бы вам помочь.