Sql 帽子
乍一看,这段代码看起来像是在构建一个动态SQL语句来获取一些数据。所谓动态,我的意思是SQL语句是在运行时构建的,而过程是基于传入的参数构造where子句 最后,他们正在做: 打开e_sql的rrc_rectyp 这基本上将查询结果放在ref游标中,这允许客户端获取结果数据 顺便说一下,以这种方式使用动态SQL对性能非常不利,因为它会导致难以解析。您可以阅读更多关于硬解析以及为什么它们在链接中是邪恶的。解决方案是使用一个上下文,这样您就可以利用绑定变量的优点并避免硬解析(这将在该链接中讨论) 编辑Sql 帽子,sql,oracle,plsql,Sql,Oracle,Plsql,乍一看,这段代码看起来像是在构建一个动态SQL语句来获取一些数据。所谓动态,我的意思是SQL语句是在运行时构建的,而过程是基于传入的参数构造where子句 最后,他们正在做: 打开e_sql的rrc_rectyp 这基本上将查询结果放在ref游标中,这允许客户端获取结果数据 顺便说一下,以这种方式使用动态SQL对性能非常不利,因为它会导致难以解析。您可以阅读更多关于硬解析以及为什么它们在链接中是邪恶的。解决方案是使用一个上下文,这样您就可以利用绑定变量的优点并避免硬解析(这将在该链接中讨论) 编
实际上,他们正在将结果数据管道化到一个集合变量中。请参阅链接,并搜索“分配表函数的结果”。它看起来像一个报告函数。它构建一个SQL语句,其中包含一些条件(WHERE中的一些元素依赖于参数) 查询本身看起来相当复杂。它使用带的
构造,允许您在查询中定义某种内联视图。这本身更多的是一个SQL(可能是Oracle SQL)特性,而不是PLSQL
然后,在游标中打开查询(在字符串变量中构建)。游标可以看作是遍历查询结果的工具,查询在这里以循环方式完成
然后,来自光标的变量被放入v_行的属性中<代码>v_行
被声明为记录类型
。它是一个可以重新表示记录的对象。对象通过管道传输到输出,这意味着此函数实际上返回一个记录集,这意味着您可以在查询中调用它,如下所示:
select * from table(monthly_analysis(<parameters>))
你确定你把所有的东西都贴出来了吗?因为现在,它永远不会成功运行。声明了很多变量,但从未使用过<例如,代码>e_sql
正在执行,但从未分配值
我希望您不要试图通过查看这些代码来学习PL/SQL,因为几乎每一行代码都会让我畏缩。特别是将变量声明为LONG(您不应该再使用它)、使用该记录以及笨拙的日期处理。哎哟,哎哟,哎哟!最重要的是,如果有人编写这样的代码,那么肯定需要学习如何评论他正在做的事情
更新 我重写了这个函数,现在它已经完成了。我用这些辅助对象测试了它:
SQL> create table ap_main
2 ( cep_work_item_no number
3 , received_date date
4 , req_accept_date date
5 , status_id number
6 , stage_id number
7 , country_id number
8 , sub_region_id number
9 , customer_type_id number
10 , customer_id number
11 , priority_id number
12 , workgroup_id number
13 , city_id number
14 )
15 /
Table created.
SQL> insert into ap_main
2 select 1,sysdate,sysdate,1,4,1,1,1,1,1,1,1 from dual union all
3 select 2,sysdate,sysdate,1,4,1,1,1,1,1,1,1 from dual union all
4 select 3,sysdate,sysdate,1,5,1,1,1,1,1,1,1 from dual union all
5 select 4,sysdate,sysdate,1,5,1,1,1,1,1,1,1 from dual union all
6 select 5,sysdate,sysdate,2,5,1,1,1,1,1,1,1 from dual union all
7 select 6,sysdate-31,sysdate-31,1,5,1,1,1,1,1,1,1 from dual union all
8 select 7,sysdate-31,sysdate-31,1,5,1,1,1,1,1,1,1 from dual union all
9 select 8,sysdate-31,sysdate-31,3,5,1,1,1,1,1,1,1 from dual
10 /
8 rows created.
SQL> create table lk_customer (customer_id,customer_name)
2 as
3 select 1, 'Anna' from dual union all
4 select 2, 'Bob' from dual
5 /
Table created.
SQL> create type analysis_report_row_type as object
2 ( months varchar2(7)
3 , orderby_months varchar2(7)
4 , req_received number
5 , req_still_open number
6 , req_await_acceptance number
7 , req_with_att number
8 , req_closed number
9 , req_cancelled number
10 )
11 /
Type created.
SQL> create type analysis_report_tab_type as table of analysis_report_row_type
2 /
Type created.
SQL> create function convert_time
2 ( p1 in date
3 , p2 in varchar2
4 , p3 in varchar2
5 ) return date
6 is
7 begin
8 return p1;
9 end;
10 /
Function created.
SQL> create package ecep_ap_utils
2 as
3 function f_business_days(p1 in date,p2 in date) return number;
4 end ecep_ap_utils;
5 /
Package created.
SQL> create package body ecep_ap_utils
2 as
3 function f_business_days(p1 in date,p2 in date) return number
4 is
5 begin
6 return p2 - p1;
7 end f_business_days;
8 end ecep_ap_utils;
9 /
Package body created.
函数的两个参数没有使用,所以我删除了它们。所有参数似乎都有错误的类型,所以我也修复了它。此外,我删除了所有不必要的变量,并使您的查询使用绑定变量。这一点很重要,因为Oracle将每个唯一的已解析语句存储在共享池中以供重用。但是,通过粘入参数,您使每个语句都是唯一的,从而导致难以解析并填满了共享池
您的函数是一个流水线函数,这在您的情况下似乎有些过分,因为您的结果集不会很大,因为您是按月份分组的。所以你每个月只能得到一排。我把它放在原处了。查询访问ap_主表六次,其中一次就足够了。您可能会注意到,通过性能提升。我仍然担心的是日期的处理。最初的程序员无法决定是使用字符串还是日期来处理日期。当然,您应该使用日期来处理日期。许多被调用的转换例程可能会以某种方式被跳过。无论如何。。。以下是新功能:
SQL> create function analysis
2 ( country_id_p in number
3 , sub_region_id_p in number
4 , customer_type_id_p in number
5 , received_from_date_p in date
6 , received_to_date_p in date
7 , customer_id_p in number
8 , priority_id_p in number
9 , work_group_id_p in number
10 , city_id_p in number
11 ) return analysis_report_tab_type pipelined
12 is
13 l_current_date date;
14 l_refcursor sys_refcursor;
15 l_analysis_report_row analysis_report_row_type := analysis_report_row_type(null,null,null,null,null,null,null,null);
16 begin
17 select sysdate + to_number(to_char(systimestamp, 'tzh')) / 24
18 into l_current_date
19 from dual
20 ;
21 open l_refcursor for
22 'select analysis_report_row_type
23 ( to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''mm/yyyy'')
24 , to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''yyyy/mm'')
25 , count(cep_work_item_no)
26 , count(case when received_date is not null and status_id=1 then 1 end)
27 , count(case when req_accept_date is null and ecep_ap_utils.f_business_days(received_date,:p_current_date)>30 then 1 end)
28 , count(case when req_accept_date is not null and status_id = 1 and stage_ID not in (4,10) and ecep_ap_utils.f_business_days(received_date,:p_current_date)>30 then 1 end)
29 , count(case when status_id = 2 then 1 end)
30 , count(case when status_id = 3 then 1 end)
31 )
32 from ap_main
33 where received_date is not null ' ||
34 case
35 when country_id_p is null then
36 ' and (1=1 or :p_country_id is null)'
37 else
38 ' and country_id = :p_country_id'
39 end ||
40 case
41 when sub_region_id_p is null then
42 ' and (1=1 or :p_sub_region_id is null)'
43 else
44 ' and sub_region_id = :p_sub_region_id'
45 end ||
46 case
47 when customer_type_id_p is null then
48 ' and (1=1 or :p_customer_type_id is null)'
49 else
50 ' and customer_type_id = :p_customer_type_id'
51 end ||
52 case
53 when received_from_date_p is null then
54 ' and (1=1 or :p_received_from_date is null)'
55 else
56 ' and convert_time(received_date, ''GMT'', ''GMT'') >= convert_time(trunc(:p_received_from_date), ''Europe/Paris'', ''GMT'')'
57 end ||
58 case
59 when received_to_date_p is null then
60 ' and (1=1 or :p_received_to_date is null)'
61 else
62 ' and convert_time(received_date, ''GMT'', ''GMT'') <= convert_time(trunc(:p_received_to_date), ''Europe/Paris'', ''GMT'')'
63 end ||
64 case
65 when customer_id_p is null then
66 ' and (1=1 or :p_customer_id is null)'
67 else
68 ' and customer_id in (select customer_id from lk_customer where upper(customer_name) like upper(:p_customer_id || ''%''))'
69 end ||
70 case
71 when priority_id_p is null then
72 ' and (1=1 or :p_priority_id is null)'
73 else
74 ' and priority_id = :p_priority_id'
75 end ||
76 case
77 when work_group_id_p is null then
78 ' and (1=1 or :p_workgroup_id is null)'
79 else
80 ' and workgroup_id = :p_workgroup_id'
81 end ||
82 case
83 when city_id_p is null then
84 ' and (1=1 or :p_city_id is null)'
85 else
86 ' and city_id = :p_city_id'
87 end ||
88 ' group by to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''mm/yyyy'')
89 , to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''yyyy/mm'')'
90 using l_current_date
91 , l_current_date
92 , country_id_p
93 , sub_region_id_p
94 , customer_type_id_p
95 , received_from_date_p
96 , received_to_date_p
97 , customer_id_p
98 , priority_id_p
99 , work_group_id_p
100 , city_id_p
101 ;
102 loop
103 fetch l_refcursor into l_analysis_report_row;
104 exit when l_refcursor%notfound;
105 pipe row (l_analysis_report_row);
106 end loop;
107 return;
108 end analysis;
109 /
Function created.
更新2 以下是指向此处使用的两个关键结构的两个链接: 公开发表声明: 流水线功能: 正如您在OpenFor语句文档中所看到的,在FOR之后,您可以指定一个查询,我将动态构造该查询。在您的原始代码中也进行了同样的操作。不同之处在于,我使用的是本机动态SQL,因此我可以使用绑定变量(以“:p_”开头的变量)。我这样做的方式是,无论我提供什么输入值,所有绑定变量都存在于查询中。以下是一个很好的解释,说明了原因和方式: 如果你还有其他问题,尽管问吧 问候,,
Rob.请发表评论,解释结束问题的原因,我准备添加更多内容或根据建议进行修改,如果这不是该问题的正确位置,请告诉我,我将相应地移动它。亲密选民,“太本地化”不是“tl;dr”的同义词,尽管以“太”开头。+1到BoltClock。PL/SQL不再被认为是StackOverflow的一个合法主题了吗?目前,三个投票中有两个赞成过于本地化,另一个不是真正的问题。我猜那些人觉得解释一段现有的代码不是SO的主要指令的一部分,因为这不太可能引起更广泛的兴趣。我本人在看到两张结束问题的投票后投票赞成结束这个问题,因为我认为要求SO读者解释其他人的代码是正确的,但我真的很难理解它,所以将其张贴在这里,以便我可以获得一些专家建议。什么does
v_row AP_ANALYSIS_REPORT_row_TYPE:=AP_ANALYSIS_REPORT_row_TYPE(null,null,…)代码>行do,它的目的是什么?那只是创建记录。因为它是一个管道函数,所以不需要创建一个集合来保存记录,但需要一条记录来保存希望为每行管道传递的值。@GolezTrol:Yes,它用于报告目的,其结果集取决于输入参数。根据您的经验,这是编写报告函数的通用方法还是有一些最佳做法?我通常会尝试阻止以这种方式构建SQL。我宁愿通过参数
SQL> create table ap_main
2 ( cep_work_item_no number
3 , received_date date
4 , req_accept_date date
5 , status_id number
6 , stage_id number
7 , country_id number
8 , sub_region_id number
9 , customer_type_id number
10 , customer_id number
11 , priority_id number
12 , workgroup_id number
13 , city_id number
14 )
15 /
Table created.
SQL> insert into ap_main
2 select 1,sysdate,sysdate,1,4,1,1,1,1,1,1,1 from dual union all
3 select 2,sysdate,sysdate,1,4,1,1,1,1,1,1,1 from dual union all
4 select 3,sysdate,sysdate,1,5,1,1,1,1,1,1,1 from dual union all
5 select 4,sysdate,sysdate,1,5,1,1,1,1,1,1,1 from dual union all
6 select 5,sysdate,sysdate,2,5,1,1,1,1,1,1,1 from dual union all
7 select 6,sysdate-31,sysdate-31,1,5,1,1,1,1,1,1,1 from dual union all
8 select 7,sysdate-31,sysdate-31,1,5,1,1,1,1,1,1,1 from dual union all
9 select 8,sysdate-31,sysdate-31,3,5,1,1,1,1,1,1,1 from dual
10 /
8 rows created.
SQL> create table lk_customer (customer_id,customer_name)
2 as
3 select 1, 'Anna' from dual union all
4 select 2, 'Bob' from dual
5 /
Table created.
SQL> create type analysis_report_row_type as object
2 ( months varchar2(7)
3 , orderby_months varchar2(7)
4 , req_received number
5 , req_still_open number
6 , req_await_acceptance number
7 , req_with_att number
8 , req_closed number
9 , req_cancelled number
10 )
11 /
Type created.
SQL> create type analysis_report_tab_type as table of analysis_report_row_type
2 /
Type created.
SQL> create function convert_time
2 ( p1 in date
3 , p2 in varchar2
4 , p3 in varchar2
5 ) return date
6 is
7 begin
8 return p1;
9 end;
10 /
Function created.
SQL> create package ecep_ap_utils
2 as
3 function f_business_days(p1 in date,p2 in date) return number;
4 end ecep_ap_utils;
5 /
Package created.
SQL> create package body ecep_ap_utils
2 as
3 function f_business_days(p1 in date,p2 in date) return number
4 is
5 begin
6 return p2 - p1;
7 end f_business_days;
8 end ecep_ap_utils;
9 /
Package body created.
SQL> create function analysis
2 ( country_id_p in number
3 , sub_region_id_p in number
4 , customer_type_id_p in number
5 , received_from_date_p in date
6 , received_to_date_p in date
7 , customer_id_p in number
8 , priority_id_p in number
9 , work_group_id_p in number
10 , city_id_p in number
11 ) return analysis_report_tab_type pipelined
12 is
13 l_current_date date;
14 l_refcursor sys_refcursor;
15 l_analysis_report_row analysis_report_row_type := analysis_report_row_type(null,null,null,null,null,null,null,null);
16 begin
17 select sysdate + to_number(to_char(systimestamp, 'tzh')) / 24
18 into l_current_date
19 from dual
20 ;
21 open l_refcursor for
22 'select analysis_report_row_type
23 ( to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''mm/yyyy'')
24 , to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''yyyy/mm'')
25 , count(cep_work_item_no)
26 , count(case when received_date is not null and status_id=1 then 1 end)
27 , count(case when req_accept_date is null and ecep_ap_utils.f_business_days(received_date,:p_current_date)>30 then 1 end)
28 , count(case when req_accept_date is not null and status_id = 1 and stage_ID not in (4,10) and ecep_ap_utils.f_business_days(received_date,:p_current_date)>30 then 1 end)
29 , count(case when status_id = 2 then 1 end)
30 , count(case when status_id = 3 then 1 end)
31 )
32 from ap_main
33 where received_date is not null ' ||
34 case
35 when country_id_p is null then
36 ' and (1=1 or :p_country_id is null)'
37 else
38 ' and country_id = :p_country_id'
39 end ||
40 case
41 when sub_region_id_p is null then
42 ' and (1=1 or :p_sub_region_id is null)'
43 else
44 ' and sub_region_id = :p_sub_region_id'
45 end ||
46 case
47 when customer_type_id_p is null then
48 ' and (1=1 or :p_customer_type_id is null)'
49 else
50 ' and customer_type_id = :p_customer_type_id'
51 end ||
52 case
53 when received_from_date_p is null then
54 ' and (1=1 or :p_received_from_date is null)'
55 else
56 ' and convert_time(received_date, ''GMT'', ''GMT'') >= convert_time(trunc(:p_received_from_date), ''Europe/Paris'', ''GMT'')'
57 end ||
58 case
59 when received_to_date_p is null then
60 ' and (1=1 or :p_received_to_date is null)'
61 else
62 ' and convert_time(received_date, ''GMT'', ''GMT'') <= convert_time(trunc(:p_received_to_date), ''Europe/Paris'', ''GMT'')'
63 end ||
64 case
65 when customer_id_p is null then
66 ' and (1=1 or :p_customer_id is null)'
67 else
68 ' and customer_id in (select customer_id from lk_customer where upper(customer_name) like upper(:p_customer_id || ''%''))'
69 end ||
70 case
71 when priority_id_p is null then
72 ' and (1=1 or :p_priority_id is null)'
73 else
74 ' and priority_id = :p_priority_id'
75 end ||
76 case
77 when work_group_id_p is null then
78 ' and (1=1 or :p_workgroup_id is null)'
79 else
80 ' and workgroup_id = :p_workgroup_id'
81 end ||
82 case
83 when city_id_p is null then
84 ' and (1=1 or :p_city_id is null)'
85 else
86 ' and city_id = :p_city_id'
87 end ||
88 ' group by to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''mm/yyyy'')
89 , to_char(convert_time(received_date, ''GMT'', ''Europe/Paris''),''yyyy/mm'')'
90 using l_current_date
91 , l_current_date
92 , country_id_p
93 , sub_region_id_p
94 , customer_type_id_p
95 , received_from_date_p
96 , received_to_date_p
97 , customer_id_p
98 , priority_id_p
99 , work_group_id_p
100 , city_id_p
101 ;
102 loop
103 fetch l_refcursor into l_analysis_report_row;
104 exit when l_refcursor%notfound;
105 pipe row (l_analysis_report_row);
106 end loop;
107 return;
108 end analysis;
109 /
Function created.
SQL> select * from table(analysis(1,1,1,null,null,1,1,1,1))
2 /
no rows selected
SQL> select * from table(analysis(null,null,null,null,null,null,null,null,null))
2 /
MONTHS ORDERBY REQ_RECEIVED REQ_STILL_OPEN REQ_AWAIT_ACCEPTANCE REQ_WITH_ATT REQ_CLOSED REQ_CANCELLED
------- ------- ------------ -------------- -------------------- ------------ ---------- -------------
12/2010 2010/12 5 4 0 0 1 0
11/2010 2010/11 3 2 0 2 0 1
2 rows selected.