Oracle 难以创建具有子查询的过程
Oracle difficulty creating a procedure that has subquery
我正在尝试构建一个将行插入 table emp_attendance.
的过程
我调用了一个根据范围生成日期列表的过程。然后我加入那个 table 和每个 employee_id.
作为新手 SQL 开发人员,我很难理解为什么没有创建过程 create_emp_attendance。
下面是我的测试案例。一旦我得到为 SELECT 工作的行,我将添加 INSERT 代码,因为我试图一次拿一小块。
在此先感谢您的帮助和专业知识。
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
CREATE OR REPLACE 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;
CREATE SEQUENCE batch_seq
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
CACHE 20
NOORDER;
Create table employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
card_num VARCHAR2(10),
work_days VARCHAR2(7)
);
INSERT INTO employees (
employee_id,
first_name,
last_name,
card_num,
work_days
)
WITH names AS (
SELECT 1, 'John', 'Doe', 'D564311','YYYYYNN' FROM dual UNION ALL
SELECT 2, 'Justin', 'Case', 'C224311','YYYYYNN' FROM dual UNION ALL
SELECT 3, 'Mike', 'Jones', 'J288811','YYYYYNN' FROM dual UNION ALL
SELECT 4, 'Jane', 'Smith', 'S564661','YYYYYNN' FROM dual
) SELECT * FROM names;
CREATE TABLE emp_attendance(
seq_num integer GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
employee_id NUMBER(6),
start_date DATE,
end_date DATE,
week_number NUMBER(2),
create_date DATE DEFAULT SYSDATE
);
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS l_batch_seq number;
BEGIN
SELECT get_batch_seq INTO l_batch_seq FROM dual;
SELECT
employee_id,
start_date,
start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date,
to_char(start_date,'WW') AS week_number
FROM (
-- Need subquery to generate end_date based on start_date.
SELECT
e.employee_id, d.COLUMN_VALUE+ NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
INNER JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date)
) d
) ed
END;
EXEC create_emp_attendanc(DATE '2021-08-07', DATE '2021-08-14');
阅读代码中的注释。
SQL> CREATE OR REPLACE PROCEDURE create_emp_attendance
2 (
3 p_start_date IN DATE,
4 p_end_date IN DATE
5 )
6 IS
7 l_batch_seq number;
8 BEGIN
9 -- there's no GET_BATCH_SEQ function (at least, you didn't post it)
10 -- SELECT get_batch_seq INTO l_batch_seq FROM dual;
11 l_batch_seq := batch_seq.nextval;
12
13 -- In order to avoid TOO_MANY_ROWS, switching to a cursor FOR loop
14 for cur_r in
15 (SELECT
16 employee_id,
17 start_date,
18 start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date,
19 to_char(start_date,'WW') AS week_number
20 FROM (-- Need subquery to generate end_date based on start_date.
21 SELECT
22 e.employee_id,
23 d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
24 FROM employees e
25 -- not INNER, but CROSS join (or, if it were INNER, on which column(s)?)
26 CROSS JOIN TABLE(generate_dates_pipelined(p_start_date, p_end_date)) d
27 ) ed
28 ) loop
29 -- you'll probably have INSERT statement here, according to what you said
30 null;
31 end loop;
32 END;
33 /
Procedure created.
测试:
SQL> EXEC create_emp_attendance(DATE '2021-08-07', DATE '2021-08-14');
PL/SQL procedure successfully completed.
SQL>
你没有提到序列,所以我删除了它。您的 SELECT
需要通过 INTO
子句具有目标或在 INSERT
(或其他)语句的上下文中使用。 JOIN
缺少 ON
子句。但是你似乎想要 CROSS JOIN
.
如果您真的想 INSERT
在一条语句中,请使用以下形式:
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO emp_attendance (employee_id, start_date, end_date, week_number)
SELECT employee_id
, start_date
, start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date
, to_char(start_date,'WW') AS week_number
FROM ( -- Need subquery to generate end_date based on start_date.
SELECT e.employee_id, d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
CROSS JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date) ) d
) ed
;
END;
/
EXEC create_emp_attendance(DATE '2021-08-07', DATE '2021-08-14');
/
-- Procedure CREATE_EMP_ATTENDANCE compiled
-- PL/SQL procedure successfully completed.
我正在尝试构建一个将行插入 table emp_attendance.
的过程我调用了一个根据范围生成日期列表的过程。然后我加入那个 table 和每个 employee_id.
作为新手 SQL 开发人员,我很难理解为什么没有创建过程 create_emp_attendance。
下面是我的测试案例。一旦我得到为 SELECT 工作的行,我将添加 INSERT 代码,因为我试图一次拿一小块。
在此先感谢您的帮助和专业知识。
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
CREATE OR REPLACE 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;
CREATE SEQUENCE batch_seq
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
CACHE 20
NOORDER;
Create table employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
card_num VARCHAR2(10),
work_days VARCHAR2(7)
);
INSERT INTO employees (
employee_id,
first_name,
last_name,
card_num,
work_days
)
WITH names AS (
SELECT 1, 'John', 'Doe', 'D564311','YYYYYNN' FROM dual UNION ALL
SELECT 2, 'Justin', 'Case', 'C224311','YYYYYNN' FROM dual UNION ALL
SELECT 3, 'Mike', 'Jones', 'J288811','YYYYYNN' FROM dual UNION ALL
SELECT 4, 'Jane', 'Smith', 'S564661','YYYYYNN' FROM dual
) SELECT * FROM names;
CREATE TABLE emp_attendance(
seq_num integer GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
employee_id NUMBER(6),
start_date DATE,
end_date DATE,
week_number NUMBER(2),
create_date DATE DEFAULT SYSDATE
);
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS l_batch_seq number;
BEGIN
SELECT get_batch_seq INTO l_batch_seq FROM dual;
SELECT
employee_id,
start_date,
start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date,
to_char(start_date,'WW') AS week_number
FROM (
-- Need subquery to generate end_date based on start_date.
SELECT
e.employee_id, d.COLUMN_VALUE+ NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
INNER JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date)
) d
) ed
END;
EXEC create_emp_attendanc(DATE '2021-08-07', DATE '2021-08-14');
阅读代码中的注释。
SQL> CREATE OR REPLACE PROCEDURE create_emp_attendance
2 (
3 p_start_date IN DATE,
4 p_end_date IN DATE
5 )
6 IS
7 l_batch_seq number;
8 BEGIN
9 -- there's no GET_BATCH_SEQ function (at least, you didn't post it)
10 -- SELECT get_batch_seq INTO l_batch_seq FROM dual;
11 l_batch_seq := batch_seq.nextval;
12
13 -- In order to avoid TOO_MANY_ROWS, switching to a cursor FOR loop
14 for cur_r in
15 (SELECT
16 employee_id,
17 start_date,
18 start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date,
19 to_char(start_date,'WW') AS week_number
20 FROM (-- Need subquery to generate end_date based on start_date.
21 SELECT
22 e.employee_id,
23 d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
24 FROM employees e
25 -- not INNER, but CROSS join (or, if it were INNER, on which column(s)?)
26 CROSS JOIN TABLE(generate_dates_pipelined(p_start_date, p_end_date)) d
27 ) ed
28 ) loop
29 -- you'll probably have INSERT statement here, according to what you said
30 null;
31 end loop;
32 END;
33 /
Procedure created.
测试:
SQL> EXEC create_emp_attendance(DATE '2021-08-07', DATE '2021-08-14');
PL/SQL procedure successfully completed.
SQL>
你没有提到序列,所以我删除了它。您的 SELECT
需要通过 INTO
子句具有目标或在 INSERT
(或其他)语句的上下文中使用。 JOIN
缺少 ON
子句。但是你似乎想要 CROSS JOIN
.
如果您真的想 INSERT
在一条语句中,请使用以下形式:
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO emp_attendance (employee_id, start_date, end_date, week_number)
SELECT employee_id
, start_date
, start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date
, to_char(start_date,'WW') AS week_number
FROM ( -- Need subquery to generate end_date based on start_date.
SELECT e.employee_id, d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
CROSS JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date) ) d
) ed
;
END;
/
EXEC create_emp_attendance(DATE '2021-08-07', DATE '2021-08-14');
/
-- Procedure CREATE_EMP_ATTENDANCE compiled
-- PL/SQL procedure successfully completed.