У меня есть приложение, в котором я нахожу сумму() столбца базы данных для набора записей, а затем использую эту сумму в отдельном запросе, похожем на следующий (составленные таблицы, но идея такая же):
SELECT Sum(cost)
INTO v_cost_total
FROM materials
WHERE material_id >=0
AND material_id <= 10;
[a little bit of interim work]
SELECT material_id, cost/v_cost_total
INTO v_material_id_collection, v_pct_collection
FROM materials
WHERE material_id >=0
AND material_id <= 10
FOR UPDATE;
Однако теоретически кто-то может обновить столбец затрат в таблице материалов между двумя запросами, и в этом случае вычисленные проценты будут отключены.
В идеале я бы просто использовал предложение FOR UPDATE в первом запросе, но когда я попробую это, я получаю сообщение об ошибке:
ORA-01786: FOR UPDATE of this query expression is not allowed
Теперь обход - это не проблема. Просто выполните дополнительный запрос, чтобы заблокировать строки, прежде чем найти Sum(), но этот запрос не будет служить никакой другой цели, кроме блокировки таблиц. Хотя этот конкретный пример не требует больших затрат времени, дополнительный запрос может привести к поражению производительности в определенных ситуациях, и это не так чисто, поэтому я бы хотел избежать этого.
Кто-нибудь знает по какой-то причине, почему это не разрешено? В моей голове предложение FOR UPDATE должно просто блокировать строки, которые соответствуют предложению WHERE - я не понимаю, почему это важно, что мы делаем с этими строками.
EDIT: похоже, что SELECT... FOR UPDATE может использоваться с аналитическими функциями, как это предлагает Дэвид Олдридж ниже. Здесь тест script я использовал для доказательства этого.
SET serveroutput ON;
CREATE TABLE materials (
material_id NUMBER(10,0),
cost NUMBER(10,2)
);
ALTER TABLE materials ADD PRIMARY KEY (material_id);
INSERT INTO materials VALUES (1,10);
INSERT INTO materials VALUES (2,30);
INSERT INTO materials VALUES (3,90);
<<LOCAL>>
DECLARE
l_material_id materials.material_id%TYPE;
l_cost materials.cost%TYPE;
l_total_cost materials.cost%TYPE;
CURSOR test IS
SELECT material_id,
cost,
Sum(cost) OVER () total_cost
FROM materials
WHERE material_id BETWEEN 1 AND 3
FOR UPDATE OF cost;
BEGIN
OPEN test;
FETCH test INTO l_material_id, l_cost, l_total_cost;
Dbms_Output.put_line(l_material_id||' '||l_cost||' '||l_total_cost);
FETCH test INTO l_material_id, l_cost, l_total_cost;
Dbms_Output.put_line(l_material_id||' '||l_cost||' '||l_total_cost);
FETCH test INTO l_material_id, l_cost, l_total_cost;
Dbms_Output.put_line(l_material_id||' '||l_cost||' '||l_total_cost);
END LOCAL;
/
Что дает результат:
1 10 130
2 30 130
3 90 130