分层 Oracle 插入。确保每个 parent 最多有一个 child

Hierarchical Oracle inserts. Ensure that every parent has at most one child

我的示例数据库有一个存储员工的 table,其中 employee_id 是主键,manager_id 是 employee_id

下图是某公司的层级结构。 employee_id没有经理(是老板!),1021管理1022。

当我执行

SELECT last_name, employee_id, manager_id, LEVEL
      FROM my_employees
      START WITH employee_id = 1020
      CONNECT BY PRIOR employee_id = manager_id;

我要收回公司层级:

LAST_NAME                 EMPLOYEE_ID MANAGER_ID      LEVEL
------------------------- ----------- ---------- ----------
Test                             1020                     1
Test2                            1021       1020          2
Test3                            1022       1021          3

我想增强此功能以验证每个 parent(即 EMPLOYEE_ID)将只有唯一的 (ONE) child。

例如,这在我的 table:

中是可能的
LAST_NAME                 EMPLOYEE_ID MANAGER_ID      LEVEL
------------------------- ----------- ---------- ----------
Test                             1020                     1
Test2                            1021       1020          2
Test3                            1022       1021          3
Test4                            1023       1021          3

Test4和Test3有同一个manager(test3和test4是同一级别的)。

我的要求是在数据库架构中找到一种简单的方法,以确保 "an employee can not have two managers no matter the level" 并且一名经理不能拥有超过 1 名属于下一级的员工。

创建我的 table/trigger 和序列的 sql 脚本如下:

--------------------------------------------------------
--  DDL for Sequence MY_EMPLOYEES_SEQ
--------------------------------------------------------

   CREATE SEQUENCE  "HR"."MY_EMPLOYEES_SEQ"  MINVALUE 1 MAXVALUE 999999999999999 INCREMENT BY 1 START WITH 1020 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  GLOBAL ;


--------------------------------------------------------
--  DDL for Table MY_EMPLOYEES
--------------------------------------------------------

  CREATE TABLE "HR"."MY_EMPLOYEES" 
   (    "EMPLOYEE_ID" NUMBER(6,0), 
    "FIRST_NAME" VARCHAR2(20 BYTE), 
    "LAST_NAME" VARCHAR2(25 BYTE), 
    "EMAIL" VARCHAR2(25 BYTE), 
    "PHONE_NUMBER" VARCHAR2(20 BYTE), 
    "HIRE_DATE" DATE, 
    "SALARY" NUMBER(8,0), 
    "MANAGER_ID" NUMBER(6,0)
   ) SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;

   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."EMPLOYEE_ID" IS 'The primary key';
   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."FIRST_NAME" IS 'First Name';
   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."LAST_NAME" IS 'Last Name';
   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."EMAIL" IS 'Email Id';
   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."PHONE_NUMBER" IS 'Phone Number
';
   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."HIRE_DATE" IS 'Hire Date';
   COMMENT ON COLUMN "HR"."MY_EMPLOYEES"."SALARY" IS 'Enfornced by check';
--------------------------------------------------------
--  DDL for Index MY_EMPLOYEES_UK1
--------------------------------------------------------

  CREATE UNIQUE INDEX "HR"."MY_EMPLOYEES_UK1" ON "HR"."MY_EMPLOYEES" ("EMAIL") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;
--------------------------------------------------------
--  DDL for Index MY_EMPLOEES_PK
--------------------------------------------------------

  CREATE UNIQUE INDEX "HR"."MY_EMPLOEES_PK" ON "HR"."MY_EMPLOYEES" ("EMPLOYEE_ID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;
--------------------------------------------------------
--  DDL for Trigger MY_EMPL_TRIG_1
--------------------------------------------------------

  CREATE OR REPLACE EDITIONABLE TRIGGER "HR"."MY_EMPL_TRIG_1" 
   before insert on "HR"."MY_EMPLOYEES" 
   for each row 
begin  
   if inserting then 
      if :NEW."EMPLOYEE_ID" is null then 
         select MY_EMPLOYEES_SEQ.nextval into :NEW."EMPLOYEE_ID" from dual; 
      end if; 
   end if; 
end;


/
ALTER TRIGGER "HR"."MY_EMPL_TRIG_1" ENABLE;
--------------------------------------------------------
--  Constraints for Table MY_EMPLOYEES
--------------------------------------------------------

  ALTER TABLE "HR"."MY_EMPLOYEES" MODIFY ("EMPLOYEE_ID" NOT NULL ENABLE);
  ALTER TABLE "HR"."MY_EMPLOYEES" MODIFY ("LAST_NAME" NOT NULL ENABLE);
  ALTER TABLE "HR"."MY_EMPLOYEES" MODIFY ("EMAIL" NOT NULL ENABLE);
  ALTER TABLE "HR"."MY_EMPLOYEES" MODIFY ("HIRE_DATE" NOT NULL ENABLE);
  ALTER TABLE "HR"."MY_EMPLOYEES" ADD CONSTRAINT "MY_EMPLOEES_PK" PRIMARY KEY ("EMPLOYEE_ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"  ENABLE;
  ALTER TABLE "HR"."MY_EMPLOYEES" ADD CONSTRAINT "MY_EMPLOYEES_UK1" UNIQUE ("EMAIL")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"  ENABLE;
  ALTER TABLE "HR"."MY_EMPLOYEES" ADD CONSTRAINT "MY_EMPLOYEES_CHK1" CHECK (salary > 0) ENABLE;

您只需要在 table 上创建一个 Unique Index 以确保不会重复任何员工,避免一名员工拥有多个经理。

create unique index uk_unique_employee on "HR"."MY_EMPLOYEES" (EMPLOYEE_ID); 

编辑

关于您的最后一条评论:"Jorge, what is wrong wit the table is that it possible to create a employee with a manager that does not EXIST."

所以你只需要在 MANAGER_ID 列中的 EMPLOYEE_ID 添加外键约束,例如:

alter table "HR"."MY_EMPLOYEES" 
    add constraint fk_employee_manager
        foreign key (MANAGER_ID)
        references "HR"."MY_EMPLOYEES" (EMPLOYEE_ID);

请注意,为了创建 FOREIGN KEY,该列必须是主键或在其上有索引(前面的命令确保了这一点)。