oracle - 循环

本文介绍了Oracle存储过程中的不同循环结构,包括loop、while和for循环,并通过实例展示了如何使用这些循环进行数据处理。此外,还对比了使用Cursor与for in loop在遍历表时的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

loop循环:
create or replace procedure pro_test_loop is
i number;
begin
i:=0;
loop
i:=i+1;
dbms_output.put_line(i);
if i>5 then
exit;
end if;
end loop;
end pro_test_loop;

while循环:
create or replace procedure pro_test_while is
i number;
begin
i:=0;
while i<5 loop
i:=i+1;
dbms_output.put_line(i);
end loop;
end pro_test_while;

for循环1:
create or replace procedure pro_test_for is
i number;
begin
i:=0;
for i in 1..5 loop
dbms_output.put_line(i);
end loop;
end pro_test_for;

 


for in loop:

for c_ag in (select a.agentid,
                        a.recommendagentid,
                        getagentstatus(a.agentstatus) agentstatus,
                        a.agentcode,
                        a.agentname,
                        a.agentlevelcode,
                        a.hiredate
                   from agenthis a
                  where a.bizyearmonth = p_month
                    and a.agentstatus = 'AGENTSTATUS_09'
                    and a.branchid = p_branchid
                    and (a.agentcode =p_agentcode or p_agentcode is null)
                    and (rownum <= v_num or v_num = 0)
                 
                 ) loop

//代码

end loop;

 

for loop 替代Cursor

我们在Oracle存储过程中需要遍历一张表,应该怎样做。我想大多少的人第一个念头就是Cursor。

  比如:

  create or replace procedure StudyCursor(

  resulst out integer

  ) is

  v_tablename varchar(30);

  v_tabletype varchar(11);

  cursor mycursor is select * from cat;

  begin

  open mycursor;

  loop

  fetch mycursor into v_tablename,v_tabletype;

  null; --you can use tablename and v_tabletype

  end loop;

  close mycursor;

  end StudyCursor;

  最近在看代码是,发现其实我们还有一个更方便的方法就是使用for in loop … end loop

  create or replace procedure StudyFor(

  resulst out integer

  ) is

  begin

  for emm in(select * from cat) loop

  null; --you can use emm.table_name and emm.table_type

  end loop;

  return ;

  end StudyFor;

  是不是更方便,我要使用的查询结果,只需使用emm.table_name和emm.table_type即可。

 

 

goto语句和标签

View Code
declare
  v_applicantionno varchar2(50);
begin

  for cur_me in (select *
                   from (select * from persist_test a order by ac06 desc)
                  where rownum < 10) loop
    begin
      if (cur_me.ac06 is not null) then
        v_applicantionno := cur_me.ac06;
      else
        goto end_insert;
      end if;
    exception
      when others then
        dbms_output.put_line('出错' || cur_me.ac06 || sqlerrm);
    end;
  
    begin
      insert into persist_test1 (ac06) values (v_applicantionno);
    exception
      when others then
        dbms_output.put_line(cur_me.ac06 || '插入出错');
    end;
  
    <<end_insert>>
    dbms_output.put_line(cur_me.ac06 || '对应的数据为空');
  end loop;
exception
  when others then
    dbms_output.put_line(sqlerrm || '全局出错');
end;

 

不常用:

View Code
------example 1: loops with IF statement-----------  
 set serveroutput on  
 declare   
     v_counter binary_integer := 0;  
     begin  
       loop  
         --increment loop counter by one  
         v_counter := v_counter + 1;  
         DBMS_OUTPUT.put_line('v_counter = '||v_counter);  
           
         --if exit condition yields true exit the loop  
         if v_counter  = 5 then  -- the same as : exit when v_counter = 5;  
           exit;  
         end if;  
           
         end loop;  
         --control resumes here  
         dbms_output.put_line('Done...');  
       end;  
        
 ------example 2: loops with EXIT WHEN conditions-----------  
 set serveroutput on  
 declare  
     v_course           course.courser_no%type :430;  
     v_instructor_id    instructor.instructor_id%type :102;  
     v_sec_num          section.section_no%type :=0;  
 begin  
     loop  
       --increment section number by one  
       v_sec_num := v_sec_num + 1;  
       insert into section   
              (section_no,course_no,section_no,instructor_id,  
              created_date, created_by,modified_date,  
              modified_by)  
        values  
              (section_id_seq.nextval,v_course,v_sec_num,  
              v_instructor_id,SYSDATE,USER,sysdate,user);  
        --if number of section added is four exit the loop  
        exit when v_sec_num = 4;  
      end loop;  
        
      --control resumes here  
      commit;  
 end;  
   
 --------exiample 3: loops with WHILE Loops condition---------  
 set serveroutput on   
 declare  
     c_counter binary_integer := 1;  
     v_sum     number :=0;  
     begin  
       while v_counter <= 10 loop  
            v_sum := v_sum + v_counter;  
            dbms_output.put_line('current sum is: '||v_sum);  
              
            -- increment loop counter by one  
            v_counter := v_counter + 1;  
       end loop;  
         
       -- control resumes here  
       dbms_output.put_line('the sum of integers between 1 '||  
                            'and 10 is: '||v_sum);  
 end;  
   
 ---------example 4: loop with For Loops----------  
 set serveroutput on  
 declare  
     v_factorial number := 1;  
 begin  
     --the loop counter is defined implicitly by the loop  
     --Therefore,before the loop ,the loop counter is undefined and has no value;  
     for v_counter in 1..10 loop  
       v_factorial := v_factorial * v_counter;  
       end loop;  
         
       --countrol resume here  
       dbms_output.put_line('factorial of ten is :'||v_factorial);  
 end; 

 

 

 

 

<think>好的,我现在需要帮用户解决关于在Oracle循环1到12月的问题。首先,用户可能想要生成一年的月份数据,或者在存储过程中处理每个月的数据。用户可能没有明确说明具体需求,但常见的场景包括生成月度报表、数据汇总或逐月处理业务逻辑。 我需要考虑Oracle循环的几种方法,比如使用PL/SQL的LOOP语句、FOR循环,或者更简便的CONNECT BY方法。用户可能对性能有要求,特别是如果处理大量数据的话,所以需要推荐高效的方法。 首先,PL/SQL的FOR循环是比较直观的方式,用户可能熟悉基本的循环结构,但需要示例来说明如何从1循环到12。另外,使用CONNECT BY LEVEL的方法可以在SQL层生成月份序列,不需要写存储过程,可能更适合简单的查询需求。 可能用户的需求是生成月份列表,比如显示每个月的名称或数字,或者是处理每个月的数据。例如,在报表中列出所有月份,即使某些月份没有数据。这时候用CONNECT BY生成12行数据,然后左联其他表可能更有效。 还要注意Oracle的日期函数,比如ADD_MONTHS和TO_CHAR,这些在生成月份名称或处理日期范围时很有用。用户可能需要将数字1-12转换成对应的月份名称,比如1月到12月,这时候需要用到TO_CHAR和日期格式化。 另外,用户可能需要在循环中执行DML操作,比如更新或插入每月的数据,这时候使用PL/SQL的循环更合适,可以在循环体内加入业务逻辑。需要提醒用户注意事务处理,比如COMMIT的位置,避免不必要的锁或性能问题。 还要考虑示例的清晰度,用户可能希望直接复制代码并修改,所以需要提供不同方法的示例,并说明每种方法的适用场景。例如,简单的SQL查询用CONNECT BY,复杂的逻辑处理用PL/SQL块。 可能用户对性能优化不太了解,需要指出CONNECT BY在生成固定行数时效率较高,而PL/SQL循环在处理复杂逻辑时更灵活。另外,使用虚拟表DUAL和CONNECT BY LEVEL <=12是常见的生成行数的方法,但需要确保用户理解其工作原理。 最后,检查是否有遗漏的情况,比如闰年或不同年份的处理,但用户的问题只涉及月份循环,可能不需要考虑年份。不过,在日期相关的示例中,最好使用具体的年份(比如2023)作为示例,避免歧义。 总结,需要提供多种方法,说明各自的优缺点,并给出示例代码,帮助用户根据实际需求选择最合适的解决方案。</think>在Oracle数据库中实现1-12月的循环操作,主要有以下5种实用方法,包含SQL和PL/SQL两种实现范式: --- ### 一、基础循环法(PL/SQL) ```sql DECLARE v_month NUMBER; BEGIN v_month := 1; LOOP DBMS_OUTPUT.PUT_LINE('Processing month: ' || TO_CHAR(TO_DATE(v_month,'MM'), 'MON')); v_month := v_month + 1; EXIT WHEN v_month > 12; END LOOP; END; ``` **输出结果**: ``` Processing month: JAN Processing month: FEB ... Processing month: DEC ``` --- ### 二、FOR循环优化版 ```sql BEGIN FOR i IN 1..12 LOOP DBMS_OUTPUT.PUT_LINE( TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE,'YEAR'), i-1), 'YYYY-MM') || ' → ' || TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE,'YEAR'), i-1), 'Month') ); END LOOP; END; ``` **输出示例**: ``` 2023-01 → January 2023-02 → February ... 2023-12 → December ``` --- ### 三、SQL层级联查询法(无需PL/SQL) ```sql SELECT LEVEL AS month_num, TO_CHAR(ADD_MONTHS(DATE '2023-01-01', LEVEL-1), 'MON') AS month_short, TO_CHAR(ADD_MONTHS(DATE '2023-01-01', LEVEL-1), 'MONTH') AS month_full FROM DUAL CONNECT BY LEVEL <= 12; ``` **执行结果**: | MONTH_NUM | MONTH_SHORT | MONTH_FULL | |-----------|-------------|-------------| | 1 | JAN | JANUARY | | 2 | FEB | FEBRUARY | | ... | ... | ... | | 12 | DEC | DECEMBER | --- ### 四、带业务逻辑的模板 ```sql DECLARE v_start_date DATE := DATE '2023-01-01'; BEGIN FOR i IN 1..12 LOOP -- 示例业务逻辑:统计每月订单量 EXECUTE IMMEDIATE ' SELECT COUNT(*) FROM orders WHERE order_date BETWEEN :1 AND LAST_DAY(:1) ' INTO v_order_count USING v_start_date; DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_start_date,'MON') || ': ' || v_order_count || ' orders'); v_start_date := ADD_MONTHS(v_start_date, 1); END LOOP; END; ``` --- ### 五、高级应用——生成日历维度表 ```sql CREATE TABLE dim_calendar AS SELECT TO_CHAR(ADD_MONTHS(DATE '2023-01-01', LEVEL-1), 'YYYYMM') AS month_id, TO_CHAR(ADD_MONTHS(DATE '2023-01-01', LEVEL-1), 'YYYY-MM') AS year_month, ADD_MONTHS(DATE '2023-01-01', LEVEL-1) AS start_date, LAST_DAY(ADD_MONTHS(DATE '2023-01-01', LEVEL-1)) AS end_date, TO_CHAR(ADD_MONTHS(DATE '2023-01-01', LEVEL-1), 'Q') AS fiscal_quarter FROM DUAL CONNECT BY LEVEL <= 12; ``` --- ### 关键知识点: 1. **日期函数**: - `ADD_MONTHS(date, n)`:日期加减月份 - `LAST_DAY(date)`:获取当月最后一天 - `TRUNC(date, 'YEAR')`:截断到年初 2. **性能对比**: - PL/SQL循环:适合需要逐月处理业务逻辑的场景 - CONNECT BY查询:数据仓库类批量操作效率更高 3. **注意事项**: - 时区敏感操作需用`FROM_TZ`函数 - 闰年处理:2月份天数自动适配 - 多语言环境建议使用NLS_DATE_LANGUAGE参数 实际应用中,90%的月度循环需求可通过`CONNECT BY`方案高效实现,建议优先考虑SQL层解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值