我在Oracle PL / SQL中重构了一个庞大的数据库包的一部分,并且有许多选择具有非常相似的where-statements。
一个例子(实际上是20个基础比较,另外5个左右,取决于查询):
-- Query 1
select * from data d into r_data
where
d.tm = time and
d.per = period and
d.mm = mm and
d.br = br and
d.ty = ty;
-- Query 2
select * from data d into r_data
where
d.tm = time and
d.per = period and
d.mm = mm and
d.br = br and
d.mat = mat;
正如您所看到的,在两种情况下都会比较tm,per,mm和br,我认为这将是一个明智的解决方案:
-- A function for comparing rows
function are_similar(row1 in data%rowtype, row2 in data%rowtype)
return number is
begin
if row1.tm = row2.tm and
row1.per = row2.per and
row1.mm = row2.mm and
row1.br = row2.br then
then return 1;
else return 0;
end if;
end are_similar;
-- Query 1 (v_row is data%rowtype)
select * from data d into r_data
where
are_similar(d, v_row) = 1 and
d.ty = v_row.ty;
-- Query 2 (v_row is data%rowtype)
select * from data d into r_data
where
are_similar(d, v_row) = 1 and
d.mat = v_row.mat;
但我明白了:
Error(xxx,xxx): PL/SQL: ORA-00904: "D": invalid identifier
我已经尝试使用谷歌搜索如何获得"行"超出"行标识符" (即D
)但我找不到任何东西,而且我也发现How to pass an entire row (in SQL, not PL/SQL) to a stored function?哪些陈述我试图做的事可能是不可能的,是吗?或者有没有办法以另一种方式做同样的事情? IE摆脱" where-clause重复"因为代码非常丑陋而且难以维护。
我知道使用参数创建一个新视图可以解决部分问题,但如果可能的话,我真的希望将解决方案保持在我正在使用的软件包内部。
答案 0 :(得分:1)
任何熟悉OO技术的人都可以看到你想要做的事情。您已经确定了常用代码,并试图将其重构为一个单独的模块。
当您在SQL中工作时,您正在使用不同的环境。在其他语言中被认为是聪明的东西,在SQL中并不那么聪明。反之亦然,如果它让你感觉更好。在Java,C#,C ++或任何其他专为OO环境设计的语言中,我们可以更加倾向于可维护性而不是性能,因为成本太低。
在SQL中不是这样。在SQL中执行任何其他语言所需的时间至少要长10倍。重新编写查询以使其调用之前没有的函数将显着降低查询的响应性。它可以将5毫秒的查询转换为45秒查询甚至更糟。即使是5秒的查询也是不可接受的。
您必须在SQL中注意但在其他语言中没有注意的一件事是上下文切换。这是您从SQL到包装语言供应商围绕其系统SQL的地方。这是Oracle中的PL / SQL或SQL Server中的Transact-SQL。每个系统都有一个。查询是SQL。这是一个背景。存储过程的主体是包装器语言。这是另一个背景。调用存储过程不仅仅是通过 here 执行代码,而是通过执行代码。来回切换上下文可能非常耗时。系统之间的细节不同,因此您应该熟悉系统的具体细节。
另一个不同之处在于其他语言本质上是程序性的。您确定了他们必须做什么,然后逐步定义如何做到这一点。在SQL中,您可以识别所需的数据。虽然有一些方法可以影响某些,但基础系统总体上决定了如何去做。
有许多技术可用于编写良好的响应式SQL代码。重写查询以调用每行的存储过程不是其中之一。