Лучший способ обновить таблицу значениями, рассчитанными из той же таблицы - PullRequest
4 голосов
/ 26 октября 2011

Я совершенно нуб в sql. Я думаю, что лучший способ сделать это; У меня есть квитанция с этими полями:

Receipt
-------
ReceiptID, AssociatedReceiptID, Value, Total

Я хочу обновить поле Total всех строк с одним и тем же AssociatedReceiptID с суммой их полей Value. Итак, я попробовал следующий SQL-оператор:

UPDATE Receipt r1
SET Total = (SELECT sum(Value)
             FROM Receipt r2
             WHERE r2.AssociatedReceiptID = r1.AssociatedReceiptID
             GROUP BY r2.AssociatedReceiptID)

С более чем 100000 записей в этой таблице, это продолжается более 17 часов. Поскольку я обновляю ту же таблицу, которую запрашиваю, я решил разделить ее на два оператора обновлений, сохранив результат суммирования во временной таблице (которая имеет), а затем обновив таблицу квитанций с этими значениями.

UPDATE TemporaryTable t1
SET Total = (SELECT sum(Value)
             FROM Receipt r2
             WHERE r2.AssociatedReceiptID = t1.AssociatedReceiptID
             GROUP BY r2.AssociatedReceiptID)

UPDATE Receipt r1
SET Total = (SELECT Total
             FROM TemporaryTable t1
             WHERE t1.ReceiptID = r1.ReceiptID)

Используя эти утверждения, процесс обновления занимает 6-7 часов. Но я уверен, что должен быть лучший способ сделать это. Итак, вкратце, это мои вопросы:

  • Как тебе это лучше?
  • Подзапрос в операторах обновления выполняется один раз для каждой строки, не так ли? Итак, если имеется 10 строк с одинаковым AssociatedReceiptID, сумма рассчитывается 10 раз. Как рассчитать сумму только один раз для AssociatedReceiptID в операторе обновления?

Заранее спасибо.

Ответы [ 5 ]

2 голосов
/ 26 октября 2011

попробуйте создать временную таблицу в памяти:

DECLARE @temp_receipts TABLE (
AssociatedReceiptID int,
sum_value int)

тогда:

insert into @temp_receipts
SELECT AssociatedReceiptID, sum(Value)
FROM Receipt
GROUP BY AssociatedReceiptID

, а затем обновите итоги основной таблицы:

UPDATE Receipt r
SET Total = (SELECT sum_value
             FROM @temp_receipts tt
             WHERE r.AssociatedReceiptID = tt.AssociatedReceiptID)

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

1 голос
/ 26 октября 2011

Изначально вы можете поступить так, как вы предлагаете в вопросе.

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

Вы можете прочитать о триггерах в MySQL здесь .

Вам может понадобиться использовать InnoDB в качестве механизма хранения.

Если вы не используете MySQL, проверьте ссылку, соответствующую вашей СУБД.

0 голосов
/ 16 мая 2016

Для больших таблиц копирование таблицы в новую (и одновременное внесение изменений) намного быстрее (по крайней мере, в БД Oracle), чем ее обновление.

Например:

update table1 set some_num = some_num +1 where year = 2010;

намного медленнее, чем:

create table table1b as
  select (case when year = 2010 then some_num+1 else some_num) as some_num,
      other, columns, of, the, table
  from table1;
drop table1;
rename table1b to table1; -- also fix or recreate constraints

(это также верно для удаления строк из таблицы: скопируйте все строки, которые должны остаться в новой таблице, а затем переименуйте ее вместо обычного удаления в исходной таблице)

Так что в вашем случае это будет:

create table ReceiptNew as
    select ReceiptID, AssociatedReceiptID, Value,
        sum(value) over (partition by AssociatedReceiptID) 
        as Total
    from Receipt;

drop table Receipt;
rename ReceiptNew to Receipt;

Опять же, вы должны заново установить ограничения для таблицы (кроме «NOT NULL», они переносятся автоматически).

0 голосов
/ 13 мая 2014

Я знаю, что это старый вопрос, но я думаю, что лучше это.

UPDATE r1
    SET r1.Total = r2.sumValue

FROM Receipt r1
    INNER JOIN 
        (SELECT sum(Value) sumValue,AssociatedReceiptID
        FROM Receipt rSum
        GROUP BY rSum.AssociatedReceiptID) r2 ON r2.AssociatedReceiptID = r1.AssociatedReceiptID

Здесь все в одном запросе и вычисление только одного.

Надеюсь, это полезно.

0 голосов
/ 26 октября 2011
MERGE INTO Receipt r
USING (
        SELECT sum(Value) s, AssociatedReceiptID
        FROM Receipt           
        GROUP BY AssociatedReceiptID
      ) r_sum 
ON( r.AssociatedReceiptID = r_sum.AssociatedReceiptID)
WHEN MATCHED THEN UPDATE
set r.Total = r_sum.s
;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...