Wednesday, November 25, 2009

Don't use PL/SQL to Do the Job of SQL

Several days ago, I was asked to review scripts from development team for an application consolidation effort. There is a particular script that uses PL/SQL, which run more than 3 hours. After I reviewed it, I believed they can be written by SQL statment.

For example, for the following PL/SQL block, it depends on the EXCEPTION condition to update a table. Though I am not an experienced PL/SQL programmer, I am suspicous this could be considered good practice in PL/SQL.

-- PL/SQL block

BEGIN
   FOR bas_rec IN bas_cur LOOP
      v_my_account_id:=bas_rec.my_account_id;

      BEGIN 
  SELECT DISTINCT ms.state_id INTO v_state_id
    FROM
      my_sch.tab_ms ms
    WHERE
      ms.account_id = v_my_account_id AND
      ms.state_id IS NOT NULL AND
      ms.svc_node_type_id  NOT IN (203,204,206,208,218,402) AND
      ms.is_pq ='N' AND
      ms.svc_status_id = 2;
     
  EXCEPTION
    WHEN TOO_MANY_ROWS THEN
      UPDATE my_sch.conv_2 bas
        SET bas.to_be_converted_status=v_exclusion_flag, 
     bas.exclusion_reason = v_exclusion_reason_multi, 
     bas.is_processed = v_processed_flag
        WHERE bas.my_account_id = v_my_account_id;
        v_is_updated:='Y';
      COMMIT;
    WHEN NO_DATA_FOUND THEN NULL; 
    WHEN OTHERS THEN 
    dbms_output.put_line('Multi State block OTHERS - v_ban:'||sqlerrm ); 
  COMMIT;
      END; 
  END LOOP;
END;


Anyway, I know that " Don't use PL/SQL to Do the Job of SQL". So I translated the above PL/SQL to the following SQL.


-- SQL code 

UPDATE my_sch.conv_2 bas
SET bas.to_be_converted_status='N', 
    bas.exclusion_reason = 'SERVICES IN MULTIPLE STATES', 
    bas.is_processed ='Y' 
WHERE bas.is_processed is null
  AND 1 < ( select count( DISTINCT ms.state_id)
      from my_ord.tab_ms ms
       WHERE ms.account_id =  bas.my_account_id
  AND ms.state_id IS NOT NULL 
  AND ms.svc_node_type_id  NOT IN (203,204,206,208,218,402) 
  AND ms.is_pq ='N' 
  AND ms.svc_status_id = 2 )
;


I also re-wrote the other part of the script with SQL. In a small scale test, original PL/SQL took 5 min. My SQL code took about 1.5 min. Sadly, developers are more comfortable with their PL/SQL code and are unwilling to do a thoroug test and verification about SQL method. So I will still use their PL/SQL code in the production implementation - just running 5 threads of them to speed up instead of 1 thread previously.

No comments: