标题确实很奇怪,但我会尽力解释一下。
我多年来一直在 11G 版本中对 Oracle (PL-SQL) 进行编程。 我最近搬到了一个新的工作场所,在第一个任务中,我被要求编写一个接受 5 个变量作为输入的函数,并且可能不会发送所有变量 - 也许只有一个。
作为该过程的一部分,在很多非常大的表之间存在非常大的查询,因此输入中的值越多,表之间的 JOINS 实现得越多,索引的使用效率也越高。 喜欢
create function get_customer(p_ssn number, p_phone number, p_email varchar2, p_address varchar2, p_unique_id number number) return <type>
is
begin
select...
from A, B, C, D, E, F, G
JOIN ..
where ssn = p_ssn
and phone = p_phone ...
end;
我想到解决该任务的第一种方法是使用动态 SQL,以便我们检查某个输入是否不为 NULL,如果是,我们可以将其 AND 链接到查询,最后对整个查询执行立即执行询问 喜欢
v_sql varchar2(4000);
v_sql := 'select...
from A, B, C, D, E, F, G
JOIN ..
WHERE 1=1'
if p_phone is not null then
v_sql := v_sql || ' AND phone = p_phone'
end if;
等等..
但是,我的新团队领导决定不使用动态 SQL,因为它很复杂并且可能会崩溃等原因。 例如,他建议对每个输入使用 NVL
select...
from A, B, C, D, E, F, G
JOIN ..
where ssn = nvl (p_ssn, a.ssn)
and phone = nvl(p_phone, b.phone) etc..
我惊呆了。我认为 DBA 团队领导应该对效率和运行时了解一两件事。 当我向他展示执行计划非常糟糕,特别是他花了很多时间才能完成运行后,他告诉我找到另一种有效的方法,而不是使用动态 SQL。
那么,其他建议 任务如何解决?
在您的示例中,您不会根据参数是否设置来改变连接哪些表;相反,您总是从相同的表中选择相同的列,并根据参数是否设置或未设置来应用不同的过滤器。
在这种情况下,传递
NULL
作为可选参数的默认值,然后使用静态查询并检查可选参数是否未使用 IS NULL
设置,以便您不应用该过滤器:
CREATE FUNCTION get_customer(
p_ssn A.SSN%TYPE DEFAULT NULL,
p_phone B.PHONE%TYPE DEFAULT NULL,
p_email C.EMAIL%TYPE DEFAULT NULL,
p_address D.ADDRESS%TYPE DEFAULT NULL,
p_unique_id E.UNIQUE_ID%TYPE DEFAULT NULL
) RETURN <type>
IS
BEGIN
SELECT A.column1,
B.column2,
C.column3,
D.column4,
E.column5,
F.column6,
G.column7
INTO ...
FROM A
INNER JOIN B ON (...)
INNER JOIN C ON (...)
INNER JOIN D ON (...)
INNER JOIN E ON (...)
INNER JOIN F ON (...)
INNER JOIN G ON (...)
WHERE (p_ssn IS NULL OR a.ssn = p_ssn)
AND (p_phone IS NULL OR b.phone = p_phone)
...;
END;
如果您想使用动态 SQL,则不要将值连接到查询中(因为这会在代码中引入 SQL 注入漏洞)。相反,使用绑定变量将值传递到查询中:
v_sql varchar2(4000);
v_sql := 'SELECT A.column1,
B.column2,
C.column3,
D.column4,
E.column5,
F.column6,
G.column7
FROM A
INNER JOIN B ON (...)
INNER JOIN C ON (...)
INNER JOIN D ON (...)
INNER JOIN E ON (...)
INNER JOIN F ON (...)
INNER JOIN G ON (...)
WHERE 1=1'
IF p_ssn IS NOT NULL THEN
v_sql := v_sql || ' AND ssn = :1';
ELSE
v_sql := v_sql || ' AND :1 IS NULL';
END IF;
IF p_phone IS NOT NULL THEN
v_sql := v_sql || ' AND phone = :2';
ELSE
v_sql := v_sql || ' AND :2 IS NULL';
END IF;
EXECUTE IMMEDIATE v_sql
INTO ...
USING p_ssn, p_phone;