我想做的是根據某個條件在SELECT中進行迭代。
CREATE OR REPLACE PROCEDURE procedure
IS
selectForLoop varchar2(256) 。
v_birthdate date;
BEGIN
select X into v_birthdate from Y where C = Z。
IF true THEN
selectForLoop := 'Select name from employees'/span>。
ELSE
selectForLoop := 'Select name from employees where birthdate = v_birthdate'/span>。
END IF;
FOR val in (selectForLoop)
回圈
業務邏輯
END LOOP。
我可以做的是:
CREATE OR REPLACE PROCEDURE procedure
IS
v_birthdate date;
BEGIN BEGIN
select X into v_birthdate from Y where C = Z。
如果true THEN
FOR i IN (Select name from employees)
回圈
雇員
END LOOP;
ELSE
FOR i IN (Select name from employees where birthdate = v_birthdate
LOOP
業務邏輯
END LOOP;
END IF;
這將是一個解決方案,但兩種方式的業務邏輯是相同的,只有我想迭代的資料是不同的。我真的希望能避免這種骯臟的復制嘗試。 有什么建議嗎?
uj5u.com熱心網友回復:
CREATE OR REPLACE PROCEDURE procedure
IS
selectForLoop varchar2(256)。
v_name employees%name%TYPE;
v_birthdatedate。
v_cursor SYS_REFCURSOR。
BEGIN
如果condition THEN
OPEN v_cursor FOR SELECT name FROM employees。
ELSE 雇員
OPEN v_cursor FOR SELECT name FROM employees WHERE birthdate =
v_birthdate。
END IF;
LOOP
FETCH v_cursor INTO v_name;
EXIT WHEN v_cursor%NOTFOUND;
業務邏輯。
END LOOP;
CLOSE v_cursor;
END;
你也可以把游標變數作為一個引數傳遞給你的業務邏輯,并把回圈移到業務邏輯程序中:
OPEN v_cursor FOR selectForLoop;
BusinessLogic(v_cursor)。
CLOSE v_cursor;
CREATE OR REPLACE
PROCEDURE BusinessLogic(p_cursor SYS_REFCURSOR IN) Is
v_name employees%name%TYPE;
BEGIN
回圈
FETCH p_cursor INTO v_name;
EXIT WHEN p_cursor%NOTFOUND。
--做業務邏輯。
END LOOP。
END;
uj5u.com熱心網友回復:
你可以在你的查詢中檢查一個引數是否為NULL。例子(基于樣本資料emp/dept)
CREATE OR REPLACE PROCEDURE list_employees (hired_after_i DATE DEFAUlT NULL)
AS[/span]BEGIN
FOR r IN
(SELECT * FROM emp
WHERE hiredate >/span> hired_after_i OR
hired_after_i IS NULL)
回圈
dbms_output.put_line(r.ename||' hired on '||r.hiredate) 。
END LOOP;
END;
/
程式LIST_EMPLOYEES 編譯。
set serveroutput on size 999999
BEGIN
list_employees;
END。
/
SMITH聘用on 17-DEC-1980
阿倫受聘于on 20-FEB-1981
WARD受聘于on 22-FEB-1981
JONES受聘于on 02-APR-1981
馬丁受聘于on 28-SEP-1981
BLAKE聘用on 01-MAY-1981
克拉克受聘于on 09-JUN-1981
SCOTT受聘于on 19-APR-1987
KING受聘于on 17-NOV1981
TURNER受聘于on 08-SEP-1981<>
ADAMS受聘于on 23-MAY-1987
JAMES受聘于on 03-DEC-1981
FORD聘用on 03-DEC1981
MILLER聘用on 23-JAN-1982
PL/SQL 程式成功完成。
BEGIN
list_employees(hired_after_i => DATE'1982-01-01')。)
END。
/
SCOTT受聘于on 19-APR-1987。
ADAMS受聘于on 23-MAY-1987
MILLER聘用on 23-JAN-1982
PL/SQL 程式成功完成。
如果你想查看一個集合,但它被填充的方式不同,那么這也是一種可能性。在這個例子中,我根據一個引數的值以兩種不同的方式填充一個pl/sql集合,然后在集合中回圈一次。
create or replace PROCEDURE list_employees2 (some_input_variable VARCHAR2)
as
TYPE emp_t IS TABLE OF emp.ename% TYPE INDEX BY BINARY_INTEGER;
t_emp emp_t;
BEGIN
IF some_input_variable = 'A THEN
SELECT ename
BULK COLLECT INTO t_emp
FROM emp WHERE hiredate > DATE'1982-01-01';
ELSIF some_input_variable = 'B'/span> THEN
t_emp(1) := 'KOEN';
t_emp(2) := 'MIKE';
END IF;
FOR i IN 1 ... t_emp.COUNT LOOP
dbms_output.put_line(t_emp(i))。
END LOOP;
END;
/
uj5u.com熱心網友回復:
你有兩個目標
。你有兩個目標
保持代碼
。干燥和使用系結變數
。
如果你只有一個非常簡單的條件(如你的例子,使用過濾器或不使用過濾器),你可以使用IF陳述句為兩種情況打開不同的游標。
IF salary_from is null THEN
OPEN v_cursor FOR SELECT LAST_NAME, SALARY FROM hr.employees;
ELSE[/span
OPEN v_cursor FOR SELECT LAST_NAME, SALARY FROM hr. employees WHERE SALARY >= salary_from;
END IF;
注意,你應該注意使用ORsolution的謂詞SALARY >= salary_from OR salary_from is NULL
為什么? 你用一個查詢來處理兩種非常不同的情況。游標可以回傳所有的資料或非常有限的資料,這可能需要一個不同的訪問方法(例如,索引訪問與全表掃描)。因此,你可能并在一種情況下使用次優計劃。
上述方法的問題是,它不能擴展。如果你有四個可選標準,你將需要16倍的IF,而且是高度冗余的代碼。
什么是解決方案?
什么是保持上述目標有效的解決方案?
使用動態SQL,但不要連接標準值,例如
SELECT LAST_NAME, SALARY FROM hr. employees WHERE salary > = 1000 and salary <= 10000
這將使系結變數的目標失效!
為了保持這個例子,可選引數salary_from和salary_to你要使用
open v_cursor for v_sql using salary_from, salary_to;
但是這要求,兩個系結變數必須在查詢文本中定義--如果你只有salary_from,應該怎么做?
為這個查詢打開游標
SELECT LAST_NAME, SALARY FROM hr. employees WHERE SALARY >= :sary_from
將導致錯誤的發生
ORA01006: bind variable does not exist
技巧是使用dummy謂詞,總是回傳true,但包含一個系結變數(將被忽略)。
因此,如果你只有salary_from作為一個過濾器,你將創建以下動態SQL
。SELECT LAST_NAME, SALARY FROM hr. employees WHERE SALARY >= : salary_from AND (1=1 or SALARY <= : salary_to)
它包含了兩個系結變數,優化器(使用*快捷鍵評估)將把它簡化為所需的
。SELECT LAST_NAME, SALARY FROM hr. employees WHERE SALARY >= :sary_from
因此,工資過濾器例子的相關代碼是
。IF salary_from is NOT null THEN
v_sql := 'SELECT LAST_NAME, SALARY FROM hr.employees WHERE SALARY >= :sary_from';
ELSE。
v_sql := 'SELECT LAST_NAME, SALARY FROM hr.employees WHERE (1 = 1 or SALARY >= :sary_from)';
END IF;
如果salary_to 是 不是 空 THEN
v_sql := v_sql ||' AND SALARY <= :salary_to';
ELSE
v_sql := v_sql ||' AND (1=1 or SALARY <= :sary_to)'。
END IF;
open v_cursor for v_sql using salary_from, salary_to;
下面是這四種情況下生成的SQL的概述
--沒有過濾器。
SELECT LAST_NAME, SALARY FROM hr. employees WHERE (1 = 1 or SALARY >= : salary_from) and (1=1 or Salary <= : 薪資_to)
-- salary_from
SELECT LAST_NAME, SALARY FROM hr. employees WHERE SALARY >= : salary_from AND (1=1 or SALARY <= : 薪資_to)
--工資從,到。
SELECT LAST_NAME, SALARY FROM hr. 員工 WHERE SALARY >= :salary_from AND SALARY <= :salary_to
-- salary_to :salary_to
SELECT LAST_NAME, SALARY FROM hr. employees WHERE (1 = 1 or SALARY >= : salary_from) and SALARY <= :salary_to
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/324988.html
標籤:
