0

I am attempting to create a procedure that will INSERT multiple rows into a table from the results of a query in the procedure.

The setup below works fine but I am having difficulty inserting the employee_id, timeoff_date from the output of the query into the timeoff table.

Below is my test CASE. I'm testing in live sql so we can both have the same Oracle version. Any help 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)
     );

   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');
Beefstu
  • 804
  • 6
  • 11
  • Do you need to insert the content of `l_res` into the table? – astentx Jul 27 '21 at 20:27
  • No I_res is only dates. All the rows, which were joined after the not EXISTS logic. That is what I want inserted. I tried modifying my code with an INSERT statement fed by the SELECT in the code – Beefstu Jul 27 '21 at 21:54

1 Answers1

1

I think it might be easier to create a procedure which looks like this (Just replace the declared constants in the block with parameters in your procedure definition):

CREATE PROCEDURE create_timeoff_requests
(
  p_dStart DATE,
  p_dEnd DATE,
  p_nEmployeeID INTEGER
  p_sType VARCHAR2
)
IS
BEGIN
  INSERT INTO timeoff (employee_id, timeoff_date, timeoff_type)
    SELECT e.employee_id, do.day_off, p_sType
    FROM employees e
    CROSS JOIN (SELECT p_dStart+LEVEL AS DAY_OFF
                FROM DUAL
                CONNECT BY LEVEL <= p_dEnd - p_dStart) do
    WHERE e.employee_id = p_nEmployeeID
    AND SUBSTR(e.workdays, TO_CHAR(do.day_off, 'D'), 1) = 'Y'
    AND NOT EXISTS (SELECT 'X' FROM holidays h WHERE h.holiday_date = do.day_off);
END;
/
Del
  • 1,529
  • 1
  • 9
  • 18
  • Thanks I'll give it a shot – Beefstu Jul 27 '21 at 21:31
  • @Beefstu: Sorry about the misunderstanding. The hardcoding and the anonymous block was just me being lazy and not wanting to create objects in my DB. It was always intended to be a procedure. – Del Jul 27 '21 at 21:32
  • will this only work for 1 employee_id at a time? In my original post I was trying to get all employees. The work_days flag Y/N determine if a row was added for that day. How can it be called? Like my original post with the EXEC – Beefstu Jul 27 '21 at 21:49
  • @Beefstu Yes, this will just do 1. If you want all just remove the parameter for employee ID and comment out that part of the WHERE clause – Del Jul 27 '21 at 22:16
  • I would only like to have one place where I can traverse through dates. Can your example be modified to call my function DATE function or will that be inefficient. I'm a developer newbie and sorry about the elementary questions – Beefstu Jul 28 '21 at 03:09