【发布时间】:2014-12-30 11:01:12
【问题描述】:
我正在使用 oracle FORALL 构造来提高 PL/SQL 块的性能。我正在写下 FORALL 的“之前”和“之后”代码 -
之前:
FOR employee_rec IN (SELECT employee_id
FROM employees
WHERE department_id =
increase_salary_prc.p_department_id_in)
LOOP
check_eligibility_prc (employee_rec.employee_id, l_eligible);
IF l_eligible THEN
UPDATE employees emp
SET emp.salary = emp.salary + emp.salary * increase_salary_prc.p_increase_pct_in
WHERE emp.employee_id = employee_rec.employee_id;
END IF;
END LOOP;
之后:
TYPE employee_ids_t IS TABLE OF employees.employee_id%TYPE INDEX BY PLS_INTEGER;
l_employee_ids employee_ids_t;
l_eligible_ids employee_ids_t;
BEGIN
SELECT employee_id
BULK COLLECT INTO l_employee_ids
FROM employees
WHERE department_id = increase_salary_prc.p_department_id_in;
FOR indx IN 1..l_employee_ids.COUNT
LOOP
check_eligibility_prc (l_employee_ids(indx),
l_eligible);
IF l_eligible THEN
l_eligible_ids(l_eligible_ids.COUNT + 1) := l_employee_ids(indx);
END IF;
END LOOP;
FORALL indx IN 1..l_eligible_ids.COUNT
UPDATE employees emp
SET emp.salary = emp.salary + emp.salary * increase_salary_prc.p_increase_pct_in
WHERE emp.employee_id = l_eligible_ids(indx);
附加信息 -
员工记录总数 - 20000 有 2 个部门 - 15 和 16,每个部门包含 10000 条记录。程序仅针对部门 15 运行。在 10000 个中,符合更新条件的为 5000 个。
check_eligibility_prc 的代码 -
SELECT eligible
INTO l_eligible
FROM EMPLOYEES
WHERE employee_id = p_emp_id;
IF l_eligible = 'Y' THEN
p_eligible := TRUE;
ELSE
p_eligible := FALSE;
END IF;
在“之前”代码中 - 1. for循环运行10000次 2. 'IF' 内循环为真 5000 次。所以更新查询运行了 5000 次。
在“之后”代码中 - 1. for 循环运行 10000 次。 2. 'IF' 内循环为真 5000 次。 3. FORALL 循环中的 l_eligible_ids 包含 5000 条待更新记录
使用“之前”块,代码需要637.42 秒才能运行。但令人惊讶的是,“之后”代码花费了极少的额外时间。它需要637.545 秒。
任何人都可以就为什么会发生这种情况提供任何指导吗? BULK COLLECT 和 FORALL 被认为可以减少花费的时间。相反,它增加了块的执行时间。
【问题讨论】:
-
这里没有足够的信息来回答这个问题。桌子上有多少员工?每个块你跑了多少次?
check_eligibility_prc是做什么的?如果大部分时间都花在这上面,那么在上面的代码中引入FORALL很可能会收效甚微。 -
员工记录有多少,更新了多少?看起来(非常)慢的部分是
check_eligibility_prc过程,而不是循环,所以也许你应该看看它在做什么。 (每次运行一次执行时间的 0.02% 差异并不能告诉您批量版本真的很慢,其他诸如机器负载之类的事情可能会淹没它;但可以肯定地说它也没有明显更快......所以你'正在尝试优化错误的部分) -
在 'before' 代码中 - 1. for 循环运行 10000 次 2. 循环内的 'IF' 为真 5000 次。所以更新查询运行了 5000 次。在“之后”代码中 - 1. For 循环运行 10000 次。 2. 'IF' 内循环为真 5000 次。所以 FORALL 循环中的 l_eligible_ids 包含要更新的 5000 条记录
-
@NoobGeek - 请在问题中添加重要的新信息作为编辑,而不是作为评论。您是否验证了这些是它实际触及的数字,并且许多记录确实已更新?你为什么要在 PL/SQL 循环中执行此操作,为什么不只是使用
where eligible = 'Y'过滤器进行简单更新?即使是 5000 个单独的查询和更新也不应该花费 10 分钟;大概您确定是这部分速度较慢,而不是同一过程中的其他部分。 -
验证消耗时间的东西不是过程:您介意在没有 if-Part 的情况下运行“之前”版本并测量循环执行过程的时间而不进行任何更新吗?
标签: oracle oracle11g oracle11gr2