#sql #performance #oracle #sql-update
#sql #Производительность #Oracle #sql-обновление
Вопрос:
Я совершенно новичок в sql. Я думаю, что лучший способ сделать это; У меня есть таблица поступлений с этими полями:
Receipt
-------
ReceiptID, AssociatedReceiptID, Value, Total
Я хочу обновить общее поле всех строк одним и тем же AssociatedReceiptID с суммой их полей значений. Итак, я попробовал следующий оператор sql:
UPDATE Receipt r1
SET Total = (SELECT sum(Value)
FROM Receipt r2
WHERE r2.AssociatedReceiptID = r1.AssociatedReceiptID
GROUP BY r2.AssociatedReceiptID)
С более чем 100000 записей в этой таблице, это длится более 17 часов. Поскольку я обновляю ту же таблицу, которую запрашиваю, я решил разделить ее на два оператора updates, сохранив результат sum во временной таблице (которая имеет), а затем обновив таблицу поступлений этими значениями.
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 раз. Как я могу вычислить сумму только один раз для каждого ассоциированного идентификатора в инструкции update?
Заранее спасибо.
Комментарии:
1. Есть ли
AssociatedReceiptID
индекс? Если этого не произойдет, вероятно, поможет его создание. Вы могли бы избежать повторения вычислений, выполнив это в простом PL / SQL, например, получив упорядоченный курсор.2. Нет, это не так. Я забыл это проверить. Я собираюсь ее создать. Спасибо за совет.
Ответ №1:
попробуйте создать временную таблицу в памяти:
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)
Однако я бы создал таблицу с именем receipt_totals или что-то в этом роде и использовал ее вместо этого. Нет смысла иметь общее количество каждой связанной квитанции в каждой отдельной связанной строке. если вы делаете это для удобства запроса, рассмотрите возможность создания представления между квитанциями и receipt_totals
Ответ №2:
Первоначально вы можете пойти так, как вы предлагаете в вопросе.
Для каждого изменения для одной строки, я думаю, было бы лучше использовать триггеры базы данных. Они обновят значение для каждой строки.
Вы можете прочитать о триггерах в MySQL здесь .
Возможно, вам потребуется использовать InnoDB в качестве механизма хранения.
Если вы не используете MySQL, пожалуйста, проверьте ссылку, соответствующую вашей СУБД.
Ответ №3:
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
;
Ответ №4:
Я знаю, что это старый вопрос, но я думаю, что лучший способ — это.
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
Вот все в одном запросе и вычислении только одного.
Надеюсь, это полезно.
Ответ №5:
Для больших таблиц копирование таблицы в новую (и одновременное внесение изменений) выполняется намного быстрее (по крайней мере, в Oracle DB), чем ее обновление.
Например:
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», они переносятся автоматически).