问:同事告诉我下面我写的这个脚本如果只用一条SQL来处理会更快一些,在date_ended列上用MIN会有问题(可能会存在重复记录和空的情况)。这是真的吗?为什么?为什么我的代码性能如此糟糕? spool &&filename — set serveroutput on format wrapped execute dbms_output.new_line; execute dbms_output.put_line(‘In UPDAT……
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
问:同事告诉我下面我写的这个脚本如果只用一条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 < psr.date_started; -- 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行记录。
最后,我要告诉你的是,试试看,就能很容易比较每种处理方式所用的时间。如果只是处理几百行记录,可能不会感觉到太明显的区别,但如果处理几千条或几万条记录就很明显。但是从性能优化的观点看,无论何时都应该总是要避免一行一行地处理数据。
作者
相关推荐
-
Collaborate 18大会:了解甲骨文云数据库和应用的进展
在Collaborate 18大会即将举行时,我们会发现,甲骨文用户社区的技术变化会略高于平常水平。 由独立甲 […]
-
甲骨文自治数据库亮相 带来云计算新希望
早前甲骨文还不在云计算公司之列,而现在该公司正在迅速弥补其失去的时间。甲骨文的云计算核心是甲骨文自治数据库(O […]
-
Oracle TNS 错误:管理员旷日持久的战斗
TNS经常给IT管理员带来麻烦,而且很难定位。尤其是在Oracle数据库中。本文将介绍如何避免这些常见错误。
-
Notre Dame对云端SQL Server性能基准的探索实践
确立SQL Server的性能基准,对于云端迁移来说是至关重要的第一步,一位来自于University of Notre Dame 的DBA表示,他正在试图通过数据库监控软件,找出SQL server的性能基准。