oracle求逐行比值,Oracle数据库中的逐行处理问题

问:同事告诉我下面我写的这个脚本如果只用一条SQL来处理会更快一些,在date_ended列上用MIN会有问题(可能会存在重复记录和空的情况)。这是真的吗?为什么?为什么我的代码性能如此糟糕?

spool &&filename

-- set serveroutput on format wrapped execute dbms_output.new_line; execute dbms_output.put_line('In UPDATE_VLE_RESP.sql...'); execute dbms_output.new_line;

DECLARE

-- ===============================================================

-- Constants

-- ===============================================================

-- VLE AUTHOR and COAUTHOR responsibilities constants   sql_c_usage_type

constant varchar2(1)   := 'P';

sql_c_vle_auth     constant varchar2(10)  := 'VLE_AUTHOR';

sql_c_vle_coauth   constant varchar2(12)  := 'VLE_COAUTHOR';

sql_c_ps_role_object  constant varchar2(3)   := 'PPI';

sql_c_rv_domain       constant varchar2(15)  := 'PS_ROLE_OBJECTS';

-- new VLE PROG_ORG responsibilities constants

sql_c_prog_org   constant varchar2(8)   := 'PROG_ORG';

-- ===============================================================

-- Type and Subtype Declarations

-- ===============================================================

-- ===============================================================

-- Variable Declarations

-- ===============================================================

sql_commit_level           NUMBER  := 0;

sql_future_vle_resp_cnt    NUMBER  := 0;

sql_vle_upd_total          NUMBER  := 0;

sql_msg_text               VARCHAR2(1000);

sql_hdr                    VARCHAR2(1);

sql_vle_rpt_total          NUMBER  := 0;

--

-- =====================================================

-- Cursors

-- =====================================================

-- get all current PPI responsibility types of VLE_AUTHOR and VLE_COAUTHOR.

-- This will be used to close off these responsibilities and report those

-- responsibilities that have been closed off in the future before they are updated.

CURSOR c_psr_vle (

p_vle_usage_type IN MISREG_PROG_STRUCT_ROLES.usage_type%TYPE,

p_vle_auth IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_vle_coauth IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_vle_ps_role_object IN MISREG_PROG_STRUCT_ROLES.rv_ps_role_object%TYPE,

p_vle_rv_domain IN MISREG_PROG_STRUCT_ROLES.rv_domain%TYPE) IS

SELECT rowid,

psr.role_code,

psr.hple_person_code,

psr.opr_type_code, -- VLE_AUTHOR/VLE_COAUTHOR

psr.rv_ps_role_object, -- 'PPI'

psr.rv_domain, -- 'PS_ROLE_OBJECTS'

psr.ppi_programme_code,

psr.ppi_consecutive_version_no,

psr.ppi_concurrent_version_no,

psr.ppi_programme_syllabus_period,

psr.ppi_domain_psp,

psr.ppi_location_code,

psr.ppi_calendar_type_code,

psr.ppi_moa_code,

psr.date_started,

psr.date_ended

FROM misreg_prog_struct_roles psr

WHERE psr.usage_type = p_vle_usage_type

AND psr.opr_type_code IN (p_vle_auth, p_vle_coauth)

AND psr.rv_ps_role_object = p_vle_ps_role_object

AND psr.rv_domain = p_vle_rv_domain

AND SYSDATE BETWEEN psr.date_started AND NVL(psr.date_ended,SYSDATE);

--

-- =====================================================

-- Procedures and Functions

-- =====================================================

PROCEDURE show_error(

p_err_code   number,

p_err_text   varchar2)

IS

BEGIN

raise_application_error(p_err_code, p_err_text);

END show_error;

------------------------------------------------------------------------------

PROCEDURE show_sys_error(

p_err_code   number,

p_err_text   varchar2) IS

BEGIN

show_error(p_err_code => p_err_code,

p_err_text => p_err_text||' - '||

dbms_utility.format_error_stack);

END show_sys_error;

------------------------------------------------------------------------------

PROCEDURE show_msg(

p_text  VARCHAR2,

p_new_line VARCHAR2) IS

sql_buffer NUMBER := 1;

BEGIN

WHILE sql_buffer <= LENGTH(p_text) LOOP

dbms_output.put_line(substr(p_text,sql_buffer,200));

sql_buffer := sql_buffer + 200;

END LOOP;

IF p_new_line = 'Y' THEN

dbms_output.new_line;

END IF;

END show_msg;

------------------------------------------------------------------------------

-- Insert PPI responsibility type of PROG_ORG based on existing current

-- PPI responsibility type of VLE_AUTHOR/VLE_COAUTHOR. If person has both

-- responsibilities then only 1 PROG_ORG record is created for the one with

-- the earliest end date. NB. if no end date has been recorded then PROG_ORG

-- record will be created with a NULL end date.

PROCEDURE ins_prog_org_resps

(p_vle_usage_type IN MISREG_PROG_STRUCT_ROLES.usage_type%TYPE,

p_vle_auth IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_vle_coauth IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_prog_org IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_vle_ps_role_object IN MISREG_PROG_STRUCT_ROLES.rv_ps_role_object%TYPE,

p_vle_rv_domain IN MISREG_PROG_STRUCT_ROLES.rv_domain%TYPE)

IS

sql_role_code MISREG_PROG_STRUCT_ROLES.role_code%TYPE;

BEGIN

--

sql_msg_text := 'Copying VLE responsibilities to new PROG_ORG responsibilities...';

show_msg(p_text => sql_msg_text, p_new_line => 'N');

--

INSERT INTO misreg_prog_struct_roles (role_code,

usage_type,

created_by,

created_date,

hple_person_code,

opr_type_code,

rv_ps_role_object,

rv_domain,

ppi_programme_code,

ppi_consecutive_version_no,

ppi_concurrent_version_no,

ppi_programme_syllabus_period,

ppi_domain_psp,

ppi_location_code,

ppi_calendar_type_code,

ppi_moa_code,

date_started,

date_ended)

SELECT misreg_prog_struct_role_seqs.nextval, inner_view.*

FROM (SELECT DISTINCT p_vle_usage_type in_usage_type, -- 'P'

USER,

SYSDATE in_cr_date,

psr.hple_person_code,

p_prog_org in_prog_org, -- 'PROG_ORG'

p_vle_ps_role_object in_ps_role_object, -- 'PPI'

p_vle_rv_domain in_vle_rv_domain, -- 'PS_ROLE_OBJECTS'

psr.ppi_programme_code,

psr.ppi_consecutive_version_no,

psr.ppi_concurrent_version_no,

psr.ppi_programme_syllabus_period,

psr.ppi_domain_psp,

psr.ppi_location_code,

psr.ppi_calendar_type_code,

psr.ppi_moa_code,

SYSDATE in_date_started,

psr.date_ended

FROM misreg_prog_struct_roles psr

WHERE psr.usage_type = p_vle_usage_type -- 'P'

AND psr.opr_type_code IN (p_vle_auth,p_vle_coauth)

AND psr.rv_ps_role_object = p_vle_ps_role_object

AND psr.rv_domain = p_vle_rv_domain

AND SYSDATE BETWEEN psr.date_started AND NVL(psr.date_ended,SYSDATE)

AND NVL(psr.date_ended,SYSDATE) =

(SELECT MIN(NVL(psr2.date_ended,SYSDATE)) earliest_date

FROM misreg_prog_struct_roles psr2

WHERE psr.usage_type = psr2.usage_type

AND psr.hple_person_code = psr2.hple_person_code

AND psr.rv_domain = psr2.rv_domain

AND psr.rv_ps_role_object = psr2.rv_ps_role_object

AND psr.ppi_programme_code = psr2.ppi_programme_code

AND psr.ppi_consecutive_version_no = psr2.ppi_consecutive_version_no

AND psr.ppi_concurrent_version_no = psr2.ppi_concurrent_version_no

AND psr.ppi_programme_syllabus_period = psr2.ppi_programme_syllabus_period

AND psr.ppi_domain_psp = psr2.ppi_domain_psp

AND psr.ppi_location_code = psr2.ppi_location_code

AND psr.ppi_calendar_type_code = psr2.ppi_calendar_type_code

AND psr.ppi_moa_code = psr2.ppi_moa_code

AND SYSDATE BETWEEN psr2.date_started AND NVL(psr2.date_ended,SYSDATE))) inner_view;

--

sql_msg_text := '---- There were '||SQL%rowcount||' VLE responsibilities copied to new PROG_ORG responsibilities...';

show_msg(p_text => sql_msg_text, p_new_line => 'Y');

--    dbms_output.put_line(chr(10));

--    dbms_output.put_line('VLE responsibilities records copied: '||SQL%rowcount);

EXCEPTION

WHEN OTHERS THEN

rollback;

show_sys_error(-20120,

'Error: Inserting Prog Struct Roles for '||

'Usage Type: '|| p_vle_usage_type|| ' and ' ||

'Opr Type Code: '|| p_prog_org || ' and ' ||

'Prog Struct Role object: '|| p_vle_ps_role_object);

END;

------------------------------------------------------------------------------

-- Check if there are any existing future PPI responsibility types of

-- VLE_AUTHOR/VLE_COAUTHOR. If there are then no further processing is done.

FUNCTION future_vle_resp (p_vle_usage_type IN MISREG_PROG_STRUCT_ROLES.usage_type%TYPE,

p_vle_auth IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_vle_coauth IN MISREG_PROG_STRUCT_ROLES.opr_type_code%TYPE,

p_vle_ps_role_object IN MISREG_PROG_STRUCT_ROLES.rv_ps_role_object%TYPE,

p_vle_rv_domain IN MISREG_PROG_STRUCT_ROLES.rv_domain%TYPE)

RETURN NUMBER

IS

v_return   NUMBER :=0;

BEGIN

SELECT COUNT(psr.role_code)

INTO v_return

FROM misreg_prog_struct_roles psr

WHERE psr.usage_type = p_vle_usage_type -- 'P'

AND psr.opr_type_code IN (p_vle_auth,p_vle_coauth)

AND psr.rv_ps_role_object = p_vle_ps_role_object

AND psr.rv_domain = p_vle_rv_domain

AND SYSDATE 

--

RETURN v_return;

--

EXCEPTION

WHEN OTHERS THEN

rollback;

show_sys_error(-20120,

'Error: Checking future Prog Struct Roles for '||

'Usage Type: '|| p_vle_usage_type|| ' and ' ||

'Opr Type Codes: '|| p_vle_auth || ', ' || p_vle_coauth || ' and ' ||

'Prog Struct Role object: '|| p_vle_ps_role_object);

END;

--

--=====================================================

-- Main Processing

--=====================================================

BEGIN

dbms_output.enable('1000000');

-- =========================================================

-- PROCESS VLE RESPONSIBILITIES UPDATE

-- =========================================================

--

-- Check if there are any future responsibilities

sql_future_vle_resp_cnt := future_vle_resp (sql_c_usage_type,

sql_c_vle_auth,

sql_c_vle_coauth,

sql_c_ps_role_object,

sql_c_rv_domain);

--

IF sql_future_vle_resp_cnt = 0 THEN

-- Copy existing VLE responsibilities

ins_prog_org_resps (sql_c_usage_type,

sql_c_vle_auth,

sql_c_vle_coauth,

sql_c_prog_org,

sql_c_ps_role_object,

sql_c_rv_domain);

--

-- Get existing VLE responsibilities to be closed off

sql_msg_text := 'Closing off existing current VLE responsibilities...';

show_msg(p_text => sql_msg_text, p_new_line => 'Y');

--    dbms_output.put_line(chr(10));

--    dbms_output.put_line('Closing off existing VLE responsibilities...');

--

sql_hdr := 'Y';

FOR c_upd_vle in c_psr_vle(sql_c_usage_type,

sql_c_vle_auth,

sql_c_vle_coauth,

sql_c_ps_role_object,

sql_c_rv_domain) LOOP

--

sql_vle_upd_total := sql_vle_upd_total + 1;

--

IF c_upd_vle.date_ended IS NOT NULL THEN

-- current VLE responsibility has been closed off in the future so report it

IF sql_hdr = 'Y' THEN

sql_msg_text := 'Current VLE Responsibilities closed off in the future';

show_msg(p_text => sql_msg_text, p_new_line => 'N');

sql_msg_text := '-----------------------------------------------------';

show_msg(p_text => sql_msg_text, p_new_line => 'Y');

sql_hdr := 'N';

END IF;

-- keep a running total of reported recs to use in output

sql_vle_rpt_total := sql_vle_rpt_total + 1;

sql_msg_text := 'Rec Num: '|| sql_vle_rpt_total;

show_msg(p_text => sql_msg_text, p_new_line => 'N');

sql_msg_text := '----------------------';

show_msg(p_text => sql_msg_text, p_new_line => 'N');

sql_msg_text := 'Role Code: '||c_upd_vle.role_code||'. '||

'Person Code: '||c_upd_vle.hple_person_code||'. '||

'Opr Type Code: '||c_upd_vle.opr_type_code||'. '||

'PS Role Obj: '||c_upd_vle.rv_ps_role_object||'. '||

'RV Domain: '||c_upd_vle.rv_domain||'. '||

'PPI Prog Code: '||c_upd_vle.ppi_programme_code||'. '||

'PPI Cons: '||c_upd_vle.ppi_consecutive_version_no||'. '||

'PPI Conc: '||c_upd_vle.ppi_concurrent_version_no||'. '||

'PPI Period: '||c_upd_vle.ppi_programme_syllabus_period||'. '||

'PPI Domain PSP: '||c_upd_vle.ppi_domain_psp||'. '||

'PPI Location: '||c_upd_vle.ppi_location_code||'. '||

'PPI Cal Type: '||c_upd_vle.ppi_calendar_type_code||'. '||

'PPI MoA: '||c_upd_vle.ppi_moa_code||'. '||

'Date Started: '||c_upd_vle.date_started||'. '||

'Date Ended: '||c_upd_vle.date_ended||'.';

--

show_msg(p_text => sql_msg_text, p_new_line => 'Y');

-- VLE responsibilities record has been closed off in the future

END IF;

--

-- Update VLE responsibilities and close off

UPDATE misreg_prog_struct_roles

SET date_ended = (SYSDATE - 1)

WHERE rowid = c_upd_vle.rowid;

--

sql_commit_level := sql_commit_level + 1;  -- Commit after every X records

IF sql_commit_level > 1000 THEN

COMMIT;

sql_commit_level := 0;

END IF;

--

END LOOP;

--

sql_msg_text := '---- There were '||sql_vle_upd_total||' current VLE Responsibilities closed off (date_ended set to '||(sysdate-1)||')...';

show_msg(p_text => sql_msg_text, p_new_line => 'Y');

--

COMMIT;

--

ELSE

dbms_output.new_line;

sql_msg_text := 'Error: Cannot process VLE responsibilities updates.'||chr(10)||

'There are future Prog Struct Roles for...'||chr(10)||

'Usage Type: '|| sql_c_usage_type|| ' and '||

'Opr Type Codes: '|| sql_c_vle_auth || ', ' || sql_c_vle_coauth || ' and ' ||chr(10)||

'Prog Struct Role object: '|| sql_c_ps_role_object;

show_msg(p_text => sql_msg_text, p_new_line => 'Y');

--

END IF;

--

sql_msg_text := '...UPDATE_VLE_RESP.sql finished.';

show_msg(p_text => sql_msg_text, p_new_line => 'N');

END;

/

答:你同事给你建议最可能的原因是你在采用逐行处理数据的方式。如Tom Kyte喜欢说的:“逐行处理数据是越来越慢”。做任何事情一次处理一行而不是一次处理多行,原理上都会更慢一些。

可以这样理解:如果你要到杂货店买10件商品,你来到杂货店,取第一件商品,付款,拿回家,放起来,然后检查一下列表,看看你需要的下一个商品,回到商店,拿那个商品,如此直到取完了10件商品。傻子才会这么做不是吗?你最好一趟就拿起所有的商品。对整个数据集一次处理一条记录就象去杂货店10趟拿10个商品,就是太慢了。听起来很有趣,但是与实际生活中的场景类比:就象用一条SQL语句处理一批数据和用PL/SQL循环一次处理一行的道理是一样的。

你的代码中的一个线索就是,可以肯定你需要在PL/SQL循环中处理的一个或两个SQL语句基本上是一样的。他们都有基本相同的解释(Where从句)。对于要更新的行,真正要做的就是设置date_ended为不同的值,除此之外没有别的。所以,为什么要一行一行地处理呢?为什么不用带Where的select语句选出需要Update的数据,然后直接更新,而不是一次只更新一行。

基本上你要做的就是如下几步:

1、执行一个查询看是否有date_started日期早于今天的的行。

2、给一个表插入新记录(这个逻辑让我有些晕,但如果说它能象期望的一样工作,那么OK)。Min子句的用法很可能可以用分析函数实现(我比较倾向于这样做),那是我很愿意谈的整个其他主题,如果你想深入了解,并能一直跟踪的话。

3、接着,读取所有的行,就是原有的行加上新插入的行,更新每行的date_ended。

即便是最坏的情况,应该也是执行两个SQL语句就可以搞定:1)执行SQL插入你需要增加的行;2)用一条Update语句修改date_ended 。这种方式比一次处理一行的方式要节约时间和资源,节约时间和资源的多少视这个过程影响的行数多少有很大不同。

另外就是你自己多琢磨琢磨,这有助于理解逐行处理引起了额外的负担,叫做上下文交换。无论何时你从PL/SQL循环中执行SQL,Oracle都不得不在PL/SQL执行引擎和SQL执行引擎之间进行换入换出的上下文交换。在你的例子中是从Update执行语句和PL/SQL引擎间来回交换。这些交换带来了负担。即便只是1/1000秒,如果是处理几千条记录,单单是上下文交换就可以吃掉大量不必要的时间。如果使用单条SQL语句代替,就完全可以避免这些交换。当然,你还是能在PL/SQL块中执行两个或多个SQL语句,并进行例外处理等,但是真的不必用那些额外的代码来处理一次更新一行。再想想杂货店的例子,如果有100,000行记录要更新,你可以执行100,000次单独的Update语句(象你正在做的),或者一次更新那100,000行记录。

最后,我要告诉你的是,试试看,就能很容易比较每种处理方式所用的时间。如果只是处理几百行记录,可能不会感觉到太明显的区别,但如果处理几千条或几万条记录就很明显。但是从性能优化的观点看,无论何时都应该总是要避免一行一行地处理数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值