作为已经建议的MERGE 方法的更具体示例:
PROCEDURE Insert_Data(Start_Date DATE, End_Date DATE)
IS
BEGIN
MERGE INTO calendar ca
USING (
SELECT Insert_Data.Start_Date + level - 1 as cal_date
FROM dual
CONNECT BY level <= Insert_Data.End_Date - Insert_Data.Start_Date + 1
) t
ON (t.cal_date = ca.cal_date)
WHEN NOT MATCHED THEN INSERT VALUES (t.cal_date);
END Insert_Data;
它根本不需要是一个程序,但这本身似乎是一个要求。您可以将合并作为普通 SQL 运行,直接使用您的日期范围而不是通过变量。 (或作为绑定变量,取决于您运行它的方式/位置)。
USING 子句是一个生成的表,它使用常见的CONNECT BY 方法创建所提供范围内的所有日期。 The LEVEL pseudocolumn 类似于您尝试执行的循环;总体而言,内部查询将您范围内的所有日期生成为内联视图,然后您可以使用它来检查实际表。如果新记录尚未退出,则语句的其余部分只会插入该范围内的新记录。
您也可以手动执行相同的操作,但效率较低,使用 NOT EXISTS 检查:
PROCEDURE Insert_Data(Start_Date DATE, End_Date DATE)
IS
BEGIN
INSERT INTO calendar
WITH t AS (
SELECT Insert_Data.Start_Date + level - 1 as cal_date
FROM dual
CONNECT BY level <= Insert_Data.End_Date - Insert_Data.Start_Date + 1
)
SELECT cal_date
FROM t
WHERE NOT EXISTS (
SELECT 1
FROM Calendar
WHERE Calendar.cal_date = t.cal_date
);
END Insert_Data;
SQL Fiddle.
您的程序中还有一些其他问题。
这是多余的,因为您使用的是 cursor-for 循环的形式:
cal_v calendar%rowtype;
这里有一个不必要的嵌套块;我想这并没有什么伤害,但它也没有添加任何东西。第一个BEGIN、DECLARE和第一个END可以去掉(对齐有点偏):
BEGIN -- remove
DECLARE -- remove
CURSOR cal_c IS
SELECT *
FROM Calendar;
BEGIN
...
END; -- remove
END Insert_Data;
不需要外部循环和整个光标;这实际上意味着您正在重复实际上完成工作的内部循环(或尝试,无论如何都是第一次)与日历表中现有记录一样多,这是毫无意义且缓慢的:
FOR cal_v IN calc_c LOOP
FOR date_v IN Insert_Data.Start_Date..Insert_Data.End_Date LOOP
...
END LOOP;
END LOOP;
内部循环无法编译,因为您不能将日期用于范围循环,只能使用整数(给出 PLS-00382):
FOR date_v IN Insert_Data.Start_Date..Insert_Data.End_Date LOOP
最里面的选择没有INTO;这也不会编译:
SELECT * FROM calendar WHERE calc_v.calc_date = date_v;
插入需要将值括在括号中:
INSERT INTO calendar VALUES date_v;
所以如果你真的想这样做,你会这样做:
PROCEDURE Insert_Data(Start_Date DATE, End_Date DATE)
IS
tmp_date DATE;
BEGIN
FOR i IN 0..(Insert_Data.End_Date - Insert_Data.Start_Date) LOOP
BEGIN
dbms_output.put_line(i);
SELECT cal_date INTO tmp_date FROM calendar
WHERE cal_date = Insert_Data.Start_Date + i;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO calendar VALUES (Insert_Data.Start_Date + i);
END;
END LOOP;
END Insert_Data;
...但实际上,使用合并。