Monday, March 26, 2007

A common requirement when importing data into a table is to log any errors which occur due to constraint violation. This often leads to people processing data in a Cursor FOR LOOP and doing one insert at a time so that they can catch and log the error:

BEGIN
FOR source_rec IN (SELECT * FROM all_objects a)
LOOP
BEGIN
INSERT INTO t1
(col1
,col2
,col3)
VALUES
(source_rec.owner
,source_rec.object_name
,source_rec.subobject_name);
EXCEPTION
WHEN OTHERS THEN
--log error using PLVISION
plvexc.recngo;
END;
END LOOP;
END;

Another option is to use the EXCEPTIONS INTO clause of ENABLE constraint:
DECLARE
l_exc_count INTEGER;
BEGIN
--Disable unique constraint
EXECUTE IMMEDIATE 'ALTER TABLE t1 DISABLE CONSTRAINT UNQ_CONST1';
--Copy data (inc duplicates) from source table
INSERT INTO t1 (SELECT * FROM t2);
--The EXCEPTIONS table can be created using $ORACLE_HOME/rdbms/admin/utlexcpt.sql.
EXECUTE IMMEDIATE 'ALTER TABLE t1 ENABLE CONSTRAINT UNQ_CONST1 EXCEPTIONS INTO EXCEPTIONS';
--If constraint is violated it will still be disabled at this point
SELECT COUNT(1) INTO l_exc_count FROM t3;
IF l_exc_count > 0
THEN
--Copy invalid rows to errors table
EXECUTE IMMEDIATE 'CREATE TABLE errors_tab as SELECT * FROM t1,exceptions e where t1.rowid=e.row_id';

DELETE FROM t1
WHERE ROWID IN (SELECT minrid
FROM (SELECT object_id
,MIN(e.row_id) minrid
FROM EXCEPTIONS e
,t_dups t
WHERE t.ROWID = e.row_id
GROUP BY object_id));
END IF;
END;

Cool new feature in Oracle 10gR2 is the LOG ERRORS clause

Comment at asktom had the following:

insert /*+ APPEND */ into t select * from t2
LOG ERRORS REJECT LIMIT UNLIMITED;

Further details here

Friday, March 16, 2007

Record Exists

Copied from some tips put together by Chris Rudd, ultimately sourced from Steven Feuerstein
/*
Note that the following shows three ways of checking if a record exists which have very similar response times, one way (implicit cursor) which is bad, and a fifth way
( select count(*) ) which is a VERY BAD THING and will result in broken fingers for anyone found using it!
*/
SET VERIFY OFF
@ssoo
DECLARE
/* Different approaches to answering "at least one?" */
CURSOR empcur
IS
SELECT employee_id
FROM employee_big WHERE department_id = &&secondparm;
v NUMBER;
b BOOLEAN;
BEGIN
plvtmr.set_factor (&&firstparm);
plvtmr.capture;
FOR i IN 1 .. &&firstparm
LOOP
BEGIN
SELECT employee_id INTO v FROM employee_big
WHERE department_id = &&secondparm;
b := TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN b := FALSE;
WHEN TOO_MANY_ROWS THEN b := TRUE;
END;
END LOOP;
PLVtmr.show_elapsed ('Implicit');

plvtmr.capture;
FOR i IN 1 .. &&firstparm
LOOP
OPEN empcur;
FETCH empcur INTO v;
b := empcur%FOUND;
CLOSE empcur;
END LOOP;
PLVtmr.show_elapsed ('Explicit');

plvtmr.capture;
FOR i IN 1 .. &&firstparm
LOOP
SELECT COUNT(*) INTO v
FROM employee_big WHERE department_id = &&secondparm;
b := v > 0;
END LOOP;
PLVtmr.show_elapsed ('COUNT');

/* Ohio OUG Contributions.... */
plvtmr.capture;
FOR i IN 1 .. &&firstparm
LOOP
SELECT COUNT(1) INTO v
FROM employee_big WHERE department_id = &&secondparm
AND ROWNUM <> 0;
END LOOP;
PLVtmr.show_elapsed ('COUNT ROWNUM<2'); department_id =" &&secondparm);"> @atleastone 1000 20
Implicit Elapsed: .45 seconds. Factored: .00045 seconds.
Explicit Elapsed: .12 seconds. Factored: .00012 seconds.
COUNT Elapsed: 2.21 seconds. Factored: .00221 seconds.
COUNT ROWNUM<2> @atleastone 20000 20
Implicit Elapsed: 8.06 seconds. Factored: .0004 seconds.
Explicit Elapsed: 2.46 seconds. Factored: .00012 seconds.
COUNT Elapsed: 42.21 seconds. Factored: .00211 seconds.
COUNT ROWNUM<2 Elapsed: 2.42 seconds. Factored: .00012 seconds.
EXISTS Elapsed: 2.63 seconds. Factored: .00013 seconds.

* /

END;
/

Thursday, March 08, 2007

Ask Tom "Hierarchical query"

Ask Tom "Hierarchical query"

Nice bit in follow up comments about collapsing rows using analytics.