I'm using a closure table to manage an organisation structure. The organisation table is defined as: CREATE TABLE ORGANISATION ( ORGID DOM_INTLARGE NOT NULL, ORG_NME DOM_VARCHARMEDIUM NOT NULL, LEVEL_NUM DOM_INTSMALL NOT NULL, INUSE DOM_BOOLN, CONSTRAINT PK_ORGANISATION PRIMARY KEY (ORGID), CONSTRAINT UN_ORGNME UNIQUE (ORG_NME, LEVEL_NUM) );
and the closure table as: CREATE TABLE ORGCHART ( PARENTID DOM_INTLARGE NOT NULL, CHILDID DOM_INTLARGE NOT NULL, DEPTH DOM_INTSMALL NOT NULL, CONSTRAINT PK_ORGCHART PRIMARY KEY (PARENTID, CHILDID), CONSTRAINT FK_ORG2CHCHILD FOREIGN KEY (CHILDID) REFERENCES ORGANISATION (ORGID), CONSTRAINT ORG2CHPARENT FOREIGN KEY (PARENTID) REFERENCES ORGANISATION (ORGID)); If I use the statements in the following stored procedure to move a part in the middle of the structure (where DEPTH =2 in the ORGCHART table) I get a PRIMARY or UNIQUE KEY constraint "PK_ORGCHART" constraint error as the delete statement hasn't removed all the required rows. SET TERM ^ ; CREATE PROCEDURE MOVEORGANISATION_OLD ( IN_ORGTOMOVE BIGINT, IN_WHERETOMOVE BIGINT ) AS BEGIN DELETE FROM orgchart WHERE childid IN (SELECT childid FROM OrgChart WHERE parentid = :IN_OrgToMove) AND parentid IN (SELECT parentid FROM OrgChart WHERE childid = :IN_OrgToMove AND parentid != childid); INSERT INTO OrgChart (ParentId, ChildId, depth) SELECT supertree.ParentId, subtree.ChildId, subtree.DEPTH + supertree.DEPTH + 1 FROM OrgChart supertree CROSS JOIN OrgChart subtree WHERE supertree.ChildId = :IN_WhereToMove AND subtree.ParentId = :IN_OrgToMove; END ^ SET TERM ; ^ However if I first store a list of the rows to be delete from ORGCHART table into a temporary table defined as: CREATE TABLE TMP_ORGCHANGE ( ORGID DOM_INTLARGE, PARENTID DOM_INTLARGE ); and then use it in the where clause criteria of the delete statement everything works as expected (see revise procedure below). SET TERM ^ ; CREATE PROCEDURE MOVEORGANISATION ( IN_ORGTOMOVE BIGINT, IN_WHERETOMOVE BIGINT ) AS BEGIN /*List records to be removed into temporary table */ insert into TMP_ORGCHANGE (parentid, orgid) select parentid, childid FROM orgchart WHERE childid IN (SELECT childid FROM OrgChart WHERE parentid = :IN_OrgToMove) AND parentid IN (SELECT parentid FROM OrgChart WHERE childid = :IN_OrgToMove AND parentid != childid); /* now delete records from ORGCHART */ delete from ORGCHART where parentid in (select parentid from TMP_ORGCHANGE) and childid in (select orgid from TMP_ORGCHANGE); /* create the new links */ INSERT INTO OrgChart (ParentId, ChildId, depth) SELECT supertree.ParentId, subtree.ChildId, subtree.DEPTH + supertree.DEPTH + 1 FROM OrgChart supertree CROSS JOIN OrgChart subtree WHERE supertree.ChildId = :IN_WhereToMove AND subtree.ParentId = :IN_OrgToMove; END ^ SET TERM ; ^ Is there something that I'm doing wrong in the first procedure that prevents all relevant records from being deleted. Here is a sample dataset to illustrate. If I pass the values 5,2 to the first procedure the operation fails but passing them to the second procedure it succeeds. INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('1', 'Whole Organisation', '0', NULL, NULL); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('2', 'Div1', '1', '1', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('3', 'Div2', '1', '1', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('4', 'Dir11', '2', '2', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('5', 'Dir21', '2', '3', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('6', 'Spec111', '3', '4', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('7', 'Spec112', '3', '4', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('8', 'Spec211', '3', '5', '1'); INSERT INTO ORGANISATION (ORGID, ORG_NME, LEVEL_NUM, PARENTID, INUSE) VALUES ('9', 'Spec212', '3', '5', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '1', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('2', '2', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '2', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('3', '3', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '3', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('4', '4', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('2', '4', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '4', '2'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('5', '5', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('3', '5', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '5', '2'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('6', '6', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('4', '6', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('2', '6', '2'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '6', '3'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('7', '7', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('4', '7', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('2', '7', '2'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '7', '3'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('8', '8', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('5', '8', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('3', '8', '2'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '8', '3'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('9', '9', '0'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('5', '9', '1'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('3', '9', '2'); INSERT INTO ORGCHART (PARENTID, CHILDID, DEPTH) VALUES ('1', '9', '3');