[英]Oracle issue porting working SQL to procedure
I have some SQL, which works fine.我有一些 SQL,它工作正常。 Inside the SQL are hard coded dates, which I want to remove and pass them in from a procedure.
SQL 内部是硬编码的日期,我想将其从过程中删除并传入。
When I try to create the procedure, which does the inserts I am getting the following error and I don't understand why its happening.当我尝试创建执行插入操作的过程时,我收到以下错误,我不明白为什么会发生这种情况。 It's the same code in the SELECT statement with an INSERT statement right above the SELECT.
它与 SELECT 语句中的代码相同,在 SELECT 的正上方有一个 INSERT 语句。
Errors: PROCEDURE CREATE_TIMEOFF_REQUESTS
Line/Col: 7/1 PL/SQL: SQL Statement ignored
Line/Col: 10/19 PL/SQL: ORA-01744: inappropriate INTO
Below is my test case.下面是我的测试用例。 I'm testing in Live SQL so we can both have the same Oracle version.
我正在 Live SQL 中进行测试,因此我们可以使用相同的 Oracle 版本。 Any help fixing my code would be greatly appreciated.
任何修复我的代码的帮助将不胜感激。
CREATE OR REPLACE TYPE obj_date IS OBJECT (
date_val DATE
);
CREATE OR REPLACE TYPE nt_date IS TABLE OF obj_date;
create or replace function generate_dates_pipelined(
p_from in date,
p_to in date
)
return nt_date
pipelined
is
begin
for c1 in (
with calendar (start_date, end_date ) as (
select trunc(p_from), trunc(p_to) from dual
union all
select start_date + 1, end_date
from calendar
where start_date + 1 <= end_date
)
select start_date as day
from calendar
) loop
pipe row (obj_date(c1.day));
end loop;
return;
end generate_dates_pipelined;
create table holidays(
holiday_date DATE not null,
holiday_name VARCHAR2(20),
constraint holidays_pk primary key (holiday_date),
constraint is_midnight check ( holiday_date = trunc ( holiday_date ) )
);
INSERT into holidays (HOLIDAY_DATE,HOLIDAY_NAME)
WITH dts as (
select to_date('01-AUG-2021 00:00:00','DD-MON-YYYY HH24:MI:SS'), 'August 1st 2021' from dual union all
select to_date('05-AUG-2021 00:00:00','DD-MON-YYYY HH24:MI:SS'), 'August 5th 2021' from dual
)
SELECT * from dts;
Create table employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
card_num VARCHAR2(10),
work_days VARCHAR2(7)
);
ALTER TABLE employees
ADD (
CONSTRAINT employees_pk PRIMARY KEY (employee_id)
);
INSERT INTO employees (
EMPLOYEE_ID,
first_name,
last_name,
card_num,
work_days
)
WITH names AS (
SELECT 1, 'Jane', 'Doe', 'F123456', 'NYYYYYN' FROM dual UNION ALL
SELECT 2, 'Madison', 'Smith', 'R33432','NYYYYYN' FROM dual UNION ALL
SELECT 3, 'Justin', 'Case', 'C765341','NYYYYYN' FROM dual UNION ALL
SELECT 4, 'Mike', 'Jones', 'D564311','NYYYYYN' FROM dual
)
SELECT * FROM names;
create table timeoff(
seq_num integer GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
employee_id NUMBER(6),
timeoff_date DATE,
timeoff_type VARCHAR2(1) DEFAULT 'V',
constraint timeoff_chk check (timeoff_date=trunc(timeoff_date, 'dd')),
constraint timeoff_pk primary key (employee_id, timeoff_date)
);
SQL works: SQL 工作原理:
INSERT INTO timeoff (employee_id, timeoff_date)
SELECT e.employee_id,
c.date_val
FROM employees e
INNER JOIN table(generate_dates_pipelined(date '2021-08-01', DATE '2021-08-10')) c
PARTITION BY ( e.employee_id )
ON (SUBSTR(e.work_days, TRUNC(c.date_val) - TRUNC(c.date_val, 'IW') + 1, 1) = 'Y')
WHERE NOT EXISTS (
SELECT 1
FROM holidays h
WHERE c.date_val = h.holiday_date
)
ORDER BY
e.employee_id,
c.date_val
;
SELECT * from timeoff;
SEQ_NUM EMPLOYEE_ID TIMEOFF_DATE TIMEOFF_TYPE
1 1 03-AUG-21 V
2 1 04-AUG-21 V
3 1 06-AUG-21 V
4 1 07-AUG-21 V
5 1 10-AUG-21 V
6 2 03-AUG-21 V
7 2 04-AUG-21 V
8 2 06-AUG-21 V
...
...
TRUNCATE table timeoff;
Procedure with SELECT creates and RUN使用 SELECT 创建和运行的过程
CREATE OR REPLACE PROCEDURE create_timeoff_requests (start_date DATE, end_date DATE)
IS
type t_date is table of date;
l_res t_date;
BEGIN
SELECT
c.date_val
BULK COLLECT INTO l_res
FROM employees e
INNER JOIN TABLE (generate_dates_pipelined (start_date, end_date))c
PARTITION BY ( e.employee_id )
ON (SUBSTR(e.work_days, TRUNC(c.date_val) - TRUNC(c.date_val, 'IW') + 1, 1) = 'Y')
WHERE NOT EXISTS (
SELECT 1
FROM holidays h
WHERE c.date_val = h.holiday_date
)
ORDER BY
e.employee_id,
c.date_val;
-- debug
for i in 1..l_res.count loop
dbms_output.put_line(l_res(i));
end loop;
END;
EXEC create_timeoff_requests (DATE '2021-08-01', DATE '2021-08-10');
03-AUG-21
04-AUG-21
06-AUG-21
07-AUG-21
10-AUG-21
03-AUG-21
04-AUG-21
06-AUG-21
07-AUG-21
10-AUG-21
...
...
Fails to create with added INSERT statement.无法使用添加的 INSERT 语句创建。
CREATE OR REPLACE PROCEDURE create_timeoff_requests (start_date DATE, end_date DATE)
IS
type t_date is table of date;
l_res t_date;
BEGIN
INSERT INTO timeoff (employee_id, timeoff_date)
SELECT
c.date_val
BULK COLLECT INTO l_res
FROM employees e
INNER JOIN TABLE (generate_dates_pipelined (start_date, end_date))c
PARTITION BY ( e.employee_id )
ON (SUBSTR(e.work_days, TRUNC(c.date_val) - TRUNC(c.date_val, 'IW') + 1, 1) = 'Y')
WHERE NOT EXISTS (
SELECT 1
FROM holidays h
WHERE c.date_val = h.holiday_date
)
ORDER BY
e.employee_id,
c.date_val;
-- debug
for i in 1..l_res.count loop
dbms_output.put_line(l_res(i));
end loop;
END;
You do not need an object to wrap a DATE
and can just use:您不需要对象来包装
DATE
,只需使用:
CREATE TYPE nt_date IS TABLE OF DATE;
You can rewrite your calendar function to:您可以将日历函数重写为:
CREATE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
Your function to insert time off almost certainly wants to take an employee as an argument (otherwise you are inserting leave for ALL employees) and you probably want to use MERGE
so that employees do not insert multiple requests for the same day if they submit overlapping requests.您插入休假的功能几乎肯定希望将员工作为参数(否则您将为所有员工插入休假)并且您可能想要使用
MERGE
以便员工在提交重叠请求时不会在同一天插入多个请求.
CREATE PROCEDURE create_timeoff_requests (
p_employee_id IN TIMEOFF.EMPLOYEE_ID%TYPE,
p_start_date IN TIMEOFF.TIMEOFF_DATE%TYPE,
p_end_date IN TIMEOFF.TIMEOFF_DATE%TYPE
)
IS
BEGIN
MERGE INTO timeoff dst
USING (
SELECT e.employee_id,
c.COLUMN_VALUE AS timeoff_date
FROM employees e
INNER JOIN TABLE(
generate_dates_pipelined(p_start_date, p_end_date)
) c
ON (SUBSTR(e.work_days, TRUNC(c.COLUMN_VALUE) - TRUNC(c.COLUMN_VALUE, 'IW') + 1, 1) = 'Y')
WHERE NOT EXISTS (
SELECT 1
FROM holidays h
WHERE c.COLUMN_VALUE = h.holiday_date
)
AND e.employee_id = p_employee_id
) src
ON ( src.employee_id = dst.employee_id
AND src.timeoff_date = dst.timeoff_date )
WHEN NOT MATCHED THEN
INSERT (employee_id, timeoff_date)
VALUES (src.employee_id, src.timeoff_date);
END;
/
You can insert the results of a query directly into a table;您可以将查询结果直接插入到表中; or you can query them into a collection;
或者你可以将它们查询到一个集合中; but you can't do both in the same single statement.
但是您不能在同一个语句中同时执行这两项操作。
I need the collection because I am calling a pipelined function that returns 1 or more dates and I didn't know how to capture the results.
我需要该集合,因为我正在调用一个返回 1 个或多个日期的流水线函数,但我不知道如何捕获结果。
The pipelined function doesn't need a collection;流水线函数不需要集合; you're already calling that from your "SQL works" standalone version.
你已经从你的“SQLworks”独立版本中调用了它。 You can move that directly into your procedure:
您可以将其直接移动到您的程序中:
CREATE OR REPLACE PROCEDURE create_timeoff_requests (start_date DATE, end_date DATE)
IS
type t_date is table of date;
l_res t_date;
BEGIN
INSERT INTO timeoff (employee_id, timeoff_date)
SELECT e.employee_id,
c.date_val
FROM employees e
INNER JOIN table(generate_dates_pipelined(start_date, end_date)) c
PARTITION BY ( e.employee_id )
ON (SUBSTR(e.work_days, TRUNC(c.date_val) - TRUNC(c.date_val, 'IW') + 1, 1) = 'Y')
WHERE NOT EXISTS (
SELECT 1
FROM holidays h
WHERE c.date_val = h.holiday_date
);
END create_timeoff_requests;
/
The only difference is changing:唯一的区别是改变:
INNER JOIN table(generate_dates_pipelined(date '2021-08-01', DATE '2021-08-10')) c
to到
INNER JOIN table(generate_dates_pipelined(start_date, end_date)) c
so it uses the procedure arguments (though you might consider renaming those as p_start_date
and p_end_date
to match the prefix pattern used by your generate_dates_pipelined
function, and reduce the likelihood of an argument/column name clash down the line).所以它使用过程参数(尽管您可能会考虑将它们重命名为
p_start_date
和p_end_date
以匹配generate_dates_pipelined
函数使用的前缀模式,并减少参数/列名称在行中发生冲突的可能性)。 And the order by
is redundant when inserting.而且插入时
order by
是多余的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.