Есть ли способ оптимизировать этот запрос на обновление? - PullRequest
0 голосов
/ 17 июня 2010

У меня есть главная таблица с именем «parent» и связанная таблица с именем «childs»

Теперь я запускаю запрос к основной таблице, чтобы обновить некоторые значения суммой из дочерней таблицы, например так.

UPDATE master m SET
    quantity1 = (SELECT SUM(quantity1) FROM childs c WHERE c.master_id = m.id),
    quantity2 = (SELECT SUM(quantity2) FROM childs c WHERE c.master_id = m.id),
    count =  (SELECT COUNT(*) FROM childs c WHERE c.master_id = m.id)
WHERE master_id = 666;

Что работает, как и ожидалось, но не является хорошим стилем, потому что я в основном делаю несколько запросов SELECT для одного и того же результата. Есть ли способ оптимизировать это? (Сделать запрос первым и сохранить значения не вариант.

Я пробовал это:

UPDATE master m SET (quantity1, quantity2, count) = (
    SELECT SUM(quantity1), SUM(quantity2), COUNT(*)
       FROM childs c WHERE c.master_id = m.id
) WHERE master_id = 666;

но это не работает.

Обновление: вот решение, благодаря всем:

Вы можете сделать что-то вроде этого:

UPDATE master m
  INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = c.quantity1,
    master.count = 1

Если у вас есть только одна дочерняя запись за раз. Однако, если вы хотите использовать групповую функцию, такую ​​как SUM () в объединенной таблице, это не работает. Либо вы получаете «Недопустимое использование функции группы», если вы покидаете часть «group by», либо «У вас есть ошибка в синтаксисе sql, если вы используете« GROUP BY c.master_id »

-- This doesnt work :(
UPDATE master m
  INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = SUM(c.quantity1),
    master.count = COUNT(c.*)
GROUP by c.master_id

Решение - использовать JOIN с подзапросом:

UPDATE master m
  INNER JOIN 
  (
    SELECT   master_id,
             SUM(quantity1) as quantity1,
             COUNT(*) as count
    FROM     childs c 
    GROUP BY master_id
  ) c
  ON c.master_id = m.master_id
SET m.quantity1 = c.quantity1,
    m.count = c.count
WHERE   m.master_id = 666;

Но так как это вытягивает каждую строку из дочерней таблицы, издержки, вероятно, будут больше, чем использование большего количества подзапросов, как в исходном sql. Поэтому вы должны добавить предложение WHERE в объединенную таблицу, чтобы получить только нужные вам строки.

Другим интересным подходом является этот синтаксис, который делает то же самое, что и JOIN с предложением WHERE, но его следует использовать только в том случае, если вы хотите обновить все строки с одинаковыми значениями и ваш подзапрос возвращает только одну строку, поскольку результат из подзапрос добавляется к результату и может использоваться как любой столбец.

UPDATE master m,
    (
      SELECT SUM(c.quantity1) as sum_of_quantity,
      COUNT(*) as rowcount FROM child c WHERE c.master_id = 666
    ) as c
SET m.quantity1 = c.sum_of_quantity,
    m.count = c.rowcount
WHERE m.master_id = 666;

Ответы [ 3 ]

2 голосов
/ 17 июня 2010

Переписывая решение Ливена для MySQL:

UPDATE master m 
JOIN (
    SELECT  master_id
            , SUM(quantity1) as quantity1
            , SUM(quantity2) as quantity2 
            , COUNT(*) as count
    FROM    childs c 
    GROUP BY
            master_id
) c
ON c.master_id = m.master_id
SET
  m.quantity1 = c.quantity1
  ,m.quantity2 = c.quantity2
  ,m.count = c.count
WHERE   m.master_id = 666;
1 голос
/ 17 июня 2010

Я не знаю, разрешено ли это в MySQL, но SQL Server позволяет использовать результат выбора в обновлении.

UPDATE master m SET
  quantity1 = c.quantity1
  , quantity2 = c.quantity2
  , count = c.count
FROM  master m
      INNER JOIN (
        SELECT  master_id
                , quantity1 = SUM(quantity1)
                , quantity2 = SUM(quantity2)
                , count = COUNT(*) 
        FROM    childs c 
        WHERE   master_id = 666
        GROUP BY
                master_id
      ) c ON c.master_id = m.master_id
0 голосов
/ 17 июня 2010

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

Если вы также хотите вставить «новые» данные в одну и ту же поездку, посмотрите INSERT INTO ... SELECT FROM ... ON DUPLICATE KEY UPDATE ...

Если вы уже делаете вставки, если строка не существует, то это будет избыточно в этом примере.

пример:

INSERT INTO master m (id, quantity1, quantity2, count)
SELECT master_id, SUM(quantity1) q1, SUM(quantity2) q1, COUNT(*) c
   FROM childs
   GROUP BY master_id
ON DUPLICATE KEY UPDATE 
   m.quantity1 = q1,
   m.quantity2 = q2,
   m.count = c

ВНИМАНИЕ! Это непроверенный код, но я думаю, что должна быть возможность сделать обратную ссылку на результат выбора в ОБНОВЛЕНИИ.

Синтаксическая ссылка: http://dev.mysql.com/doc/refman/5.0/en/insert.html

...