0

I need to create and execute a query dynamically in a stored procedure.

I have two tables users and user_updates.

Employee table has emp_id, username, division, product, region, title etc.

Employee_updates has columns like emp_id, effective_date, column_name, new_value etc.

Basically this is what I want to do.

  1. Get all employees having updates in user_udates table for a given effective date.

  2. Loop over each employee.

  3. Get all updates for each employeefor the given effective date. A employee may have one or more than one updates in employee_updates table.

  4. Create a dynamic "UPDATE" query based on those updates like

    update employee set col1 = new_val_1, col2 = new_val_2 where emp_id = ?

This is what I have done so far

    create or replace
PROCEDURE SP_RUN_EMPLOYEE_UPDATES 
(
  IN_DATE IN DATE
) 
  IS
      
    update_sql varchar2(225); 
      
employee_id BI_EMPLOYEE_UPDATE.employee_id%TYPE;   

    CURSOR  employees 
        IS   SELECT distinct(employee_id)
               FROM BI_EMPLOYEE_UPDATE 
             WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') 
              AND EXECUTED = 'N' AND ACTIVITY_ID = '0'
              ;
   
    CURSOR e_updates 
    IS  SELECT * 
         FROM BI_EMPLOYEE_UPDATE 
        WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') 
         AND EXECUTED = 'N' 
         AND ACTIVITY_ID = '0' 
         and employee_id = employee_id ;
      
     BEGIN
      
   OPEN employees;
   LOOP
    FETCH employees into employee_id;
        EXIT WHEN employees%NOTFOUND;
            
           FOR e_update in e_updates
         update_sql :=  'UPDATE BI_EMPLOYEE SET ';
              LOOP
                -- create dynam,ic update statment
             
                 UPDATE BI_EMPLOYEE_UPDATE
                 SET EXECUTED = 'Y'
                 WHERE EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID ;
                
                END LOOP;
            
            -- run dynamic sql 
     
              END LOOP;
 CLOSE employees;
END;

PLease help.

Community
  • 1
  • 1
ajm
  • 12,863
  • 58
  • 163
  • 234
  • 1
    [Dynamic SQL](http://docs.oracle.com/cd/E14072_01/appdev.112/e10472/dynamic.htm) is for when you don't know what the SQL will look like until run-time. That doesn't seem to be the case here, so I think that word is misleading. You don't know the *values* until runtime, but that's quite normal. What specific problem do you have? The first problem I can see is that you're using the same name for a variable as for a column, which is confusing at best, and probably giving unexpected results. But the approach seems odd anyway; you'll update each `employee` multiple times? Why two loops? – Alex Poole Sep 11 '12 at 13:18

1 Answers1

4

You have a few issues here, including:

  • IN_DATE is declared as a date, so you don't need to pass it through TO_DATE().
  • You only need one cursor loop; if you want to process all updates for an employee_id together for some reason you can add an order by clause.
  • You don't need dynamic SQL at all; you can use the values from the cursor as part of a static SQL update.

So a simple version with a single loop might look something like:

CREATE OR REPLACE PROCEDURE sp_run_employee_updates (p_date IN DATE) IS
    CURSOR c_updates IS
        SELECT *
        FROM bi_employee_update
        WHERE effective_date = p_date
        AND executed = 'N' 
        AND activity_id = '0'
        FOR UPDATE;     
BEGIN
    -- loop around all pending records
    FOR r_update IN c_updates LOOP
        -- apply this update to the bi_employee record
        UPDATE bi_employee
        SET col1 = r_update.col1, col2 = r_update.col2
        WHERE emp_id = r_update.employee_id;

        -- mark this update as executed
        UPDATE bi_employee_update
        SET executed = 'Y'
        WHERE CURRENT OF c_updates;
    END LOOP;
END sp_run_employee_updates;

This is using the for update and where current of constructs to both lock the row you're working with and to simplify the update; see the documentation here.

It's worth noting that if either effective_date or p_date has a time component they won't match. It's unlikely for p_date, but harder to guess for effective_date. If it does then you either need to trunc() it, or use between to look for a range of times.

Alex Poole
  • 183,384
  • 11
  • 179
  • 318