Как объединить два оператора UPDATE в одном запросе MySQL? - PullRequest
3 голосов
/ 10 января 2011

Привет,

Как можно выполнить два оператора UPDATE в одном запросе, например:

UPDATE albums SET isFeatured = '0' WHERE isFeatured = '1'

в сочетании с

UPDATE albums SET isFeatured = '1' WHERE id = '$id'

Как правило, при добавлении нового альбома ранее выбранный альбом возвращается в обычный режим, а новый - в активный.

Спасибо!

Ответы [ 6 ]

7 голосов
/ 10 января 2011

Когда вам приходится делать подобные вещи, это показатель того, что ваша модель данных неверна и может быть исправлена.

Итак, я бы рекомендовал добавить отдельную таблицу featured_albums (FK: int id_album) и используйте его, чтобы определить, является ли альбом избранным.

Ваше обновление становится

DELETE FROM featured_album; INSERT INTO featured_album SET id_album = $id;

При выборе объединения таблиц

SELECT album.id,
       album.name, 
       ( id_album IS NOT NULL ) AS isfeatured
FROM   album
LEFT JOIN featured_album ON id_album = album.id 

В соответствии с просьбой расширить вышеупомянутое, в основном, я предлагаю добавить таблицу, которая будет содержать строку, обозначающую текущий выбранный альбом.Это отношение 1 к 1, т.е. одна запись в таблице альбомов имеет одну связанную запись в таблице feature_albums.См. Типы отношений .

Album Schema Diagram

. Вы удаляете поле isFeatured из таблицы альбома и добавляете новую таблицу.

CREATE TABLE `featured_album` (
    `id_album` INTEGER NOT NULL,
    FOREIGN KEY (id_album) REFERENCES `album` (`id`)
);

Строка DELETE FROM .. INSERT INTO устанавливает избранный альбом, создавая запись в таблице.

Оператор SELECT с LEFT JOIN извлечет записи из таблицы альбомов и объединит те, которые соответствуюттаблица featured_album, в нашем случае будет соответствовать только одна запись, так как в таблице featured_album есть одно поле, она вернет NULL для всех записей, кроме избранного альбома.

Так что, если мы сделали

SELECT album.id, album.name, featured_album.id_album as isFeatured0
FROM   album
LEFT JOIN featured_album ON id_album = album.id 

Мы получили бы что-то вроде следующего:

+----+----------------+------------+
| id | name           | isFeatured |
+----+----------------+------------+
|  1 | Rumours        |       NULL |
|  2 | Snowblind      |       NULL |
|  3 | Telegraph road |          3 |
+----+----------------+------------+

т.е. NULL для isFeatured или ID.

Добавив ( id_album IS NOT NULL ) AS isfeatured и используя первый запрос, мы получим

+----+----------------+------------+
| id | name           | isfeatured |
+----+----------------+------------+
|  1 | Rumours        |          0 |
|  2 | Snowblind      |          0 |
|  3 | Telegraph road |          1 |
+----+----------------+------------+

т. Е. 0/1 для isfeatured, что делает вещи более читабельными, хотя, если вы обрабатываете результаты в PHP, это не изменит ваш код.

7 голосов
/ 10 января 2011

Попробуйте это:

UPDATE albums SET isFeatured = IF(id!='$id', '0','1')
4 голосов
/ 10 января 2011

Вы можете использовать оператор CASE WHEN и не забывать устанавливать исходное значение там, где это необходимо (условие ELSE ниже), и упорядочивать условия CASE по мере необходимости (в приведенном ниже утверждении isFeatured будет 0, если строка с запрошенным идентификатором также имеет isFeatured = 1, чтобы изменить его, поменяйте местами предложения WHEN.

UPDATE albums 
SET isFeatured = CASE 
  WHEN isFeatured = '1' THEN '0' 
  WHEN id = '$id' THEN '1'
  ELSE isFeatured
END
1 голос
/ 10 января 2011

MySQL не может использовать индекс, когда он находится внутри функции if: вам нужен индекс для функции, который невозможен в MySQL.

см. Также: Как создать индексв части даты поля DATETIME в MySql

Я использую базу данных тестов сотрудников http://dev.mysql.com/doc/employee/en/employee.html

mysql> describe employees;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int(11)       | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.01 sec)

mysql> select count(*) from employees;
+----------+
| count(*) |
+----------+
|   300024 |
+----------+
1 row in set (0.37 sec)

Установите все полы для мужчин, чтобы это имитировало вопрос.

mysql> update employees set gender = 'M';
Query OK, 1 row affected (9.11 sec)
Rows matched: 300024  Changed: 1  Warnings: 0

mysql>  select emp_no, gender from employees order by emp_no limit 2;
+--------+--------+
| emp_no | gender |
+--------+--------+
|  10001 | M      |
|  10002 | M      |
+--------+--------+
2 rows in set (0.00 sec)

Установите одного работника на женщину.(Обратите внимание, что он использует индекс и почти мгновенно.)

mysql> update employees set gender = 'F' where emp_no = 10001;
Query OK, 1 row affected (0.14 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Теперь мы используем предложенный ответ.(Обратите внимание, что индекс не использует индекс и затрагивает каждую строку.)

mysql> update employees set gender = if(emp_no=10002, 'F', 'M');
Query OK, 2 rows affected (10.67 sec)
Rows matched: 300024  Changed: 2  Warnings: 0

Поможет ли индекс?

> mysql> create index employees_gender_idx  on employees(gender);
Query OK, 300024 rows affected (21.61 sec)
Records: 300024  Duplicates: 0  Warnings: 0

> mysql> update employees set gender = if(emp_no=10001, 'F', 'M');
Query OK, 2 rows affected (9.02 sec)
Rows matched: 300024  Changed: 2  Warnings: 0

Nope.

Также было сказано, что MySQL будет рассматривать толькостроки, которые необходимо изменить.

mysql> update employees set gender = 'M';
Query OK, 1 row affected (8.78 sec)
Rows matched: 300024  Changed: 1  Warnings: 0

Не угадайте.Что, если использовать предложение WHERE?

mysql> update employees set gender = 'M' where gender ='F';
Query OK, 0 rows affected (0.03 sec)
Rows matched: 0  Changed: 0  Warnings: 0

Ну и дела так быстро, теперь он использовал индекс.

Mysql понятия не имеет, что вернет функция IF и необходимо выполнить полное сканирование таблицы.Обратите внимание, что WHERE действительно означает, где и SET действительно означает набор.Вы не можете ожидать, что БД просто упорядочит все ваши предложения, чтобы получить хорошую производительность.

Правильное решение состоит в том, чтобы выпустить два обновления (которые, если использование индексов будет почти мгновенным.)

ПримечаниеВ другом месте было сказано, что MySQL будет магически знать, только обновлять строки, которые нужно изменить.

1 голос
/ 10 января 2011

Ты просто не можешь.Вы можете выбрать только одну группу записей, которые должны быть обновлены, а затем выполнять только одну операцию со всеми из них.Это невозможно сделать

 UPDATE x SET col1 = 1 WHERE col1 = 0 AND col1 = 0 WHERE col1 = 1;

Будьте осторожны при использовании функций, чтобы обойти это, так как они должны быть оценены для каждой строки, и это может стать очень дорогим.

0 голосов
/ 10 января 2011

Я не думаю, что вы можете, или, по крайней мере, не аккуратно или практично.

Если вы хотите сделать один вызов из php / чем угодно, тогда вы можете разделить их точкой с запятой таким образом:

UPDATE albums SET isFeatured = '0' WHERE isFeatured = '1';UPDATE albums SET isFeatured = '1' WHERE id = '$id';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...