You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL中外键多引用实现及重复列名错误解决咨询

解决ORA-00957重复列名及多外键OR引用问题

首先,你碰到的ORA-00957: duplicate column name错误原因非常清晰——你的SQL语句里重复定义了STAFFNAME列三次:每次写STAFFNAME REFERENCES ...都是在尝试创建一个新的STAFFNAME列,而同一数据库表中绝对不允许出现重复列名。先把这个基础问题修正:先一次性定义所有列,再单独添加外键约束(另外从你的关系模型来看,staffNo已经作为外键关联了Staff_ChargeNurse,STAFFNAME大概率不需要重复设置外键,除非有特殊业务要求)。

接下来是你最关心的核心问题:如何让itemNo实现强制OR关系(即itemNo必须存在于Supplies_Pharmaceutical、Supplies_Surgical、Supplies_Non-Surgical这三个表中的任意一个)。标准SQL并不支持单个外键同时引用多个父表,所以我们可以通过以下几种方案来实现需求:

方案1:使用CHECK约束+自定义函数(Oracle专属支持)

在Oracle中,你可以先创建一个函数来校验itemNo是否存在于三个表中的至少一个,再将这个函数绑定到CHECK约束上。

步骤1:创建校验函数

CREATE OR REPLACE FUNCTION is_item_valid(p_itemno CHAR(6)) RETURN BOOLEAN IS
    v_count NUMBER;
BEGIN
    -- 检查三个供应表中是否存在目标itemNo
    SELECT COUNT(*) INTO v_count
    FROM (
        SELECT itemNo FROM Supplies_Pharmaceutical WHERE itemNo = p_itemno
        UNION ALL
        SELECT itemNo FROM Supplies_Surgical WHERE itemNo = p_itemno
        UNION ALL
        SELECT itemNo FROM Supplies_Non-Surgical WHERE itemNo = p_itemno
    );
    RETURN v_count > 0;
END;
/

步骤2:创建表并添加约束

先修正列的定义,再添加外键和自定义校验约束:

CREATE TABLE REQUISITION (
    REQNO CHAR(6) CONSTRAINT REQNO_PK PRIMARY KEY,
    STAFFNO CHAR(6),
    STAFFNAME VARCHAR2(100), -- 仅定义一次列
    WARDNO CHAR(6),
    ITEMNO CHAR(6) NOT NULL,
    QUANTITY INT,
    DATEORDERED DATE,
    DATERECEIVED DATE, -- 注意你原拼写是DATERECIEVED,这里修正为正确拼写
    -- 绑定staffNo的外键约束
    CONSTRAINT REQ_STAFFNO_FK FOREIGN KEY (STAFFNO) REFERENCES STAFF_CHARGENURSE(STAFFNO),
    -- 添加itemNo有效性校验约束
    CONSTRAINT REQ_ITEMNO_VALID CHECK (is_item_valid(ITEMNO))
);

方案2:使用触发器实现验证

如果CHECK约束加函数的方式不符合你的需求,也可以用触发器在插入或更新数据时校验itemNo的有效性:

创建触发器

CREATE OR REPLACE TRIGGER trg_requisition_itemno_valid
BEFORE INSERT OR UPDATE OF ITEMNO ON REQUISITION
FOR EACH ROW
DECLARE
    v_count NUMBER;
BEGIN
    SELECT COUNT(*) INTO v_count
    FROM (
        SELECT itemNo FROM Supplies_Pharmaceutical WHERE itemNo = :NEW.ITEMNO
        UNION ALL
        SELECT itemNo FROM Supplies_Surgical WHERE itemNo = :NEW.ITEMNO
        UNION ALL
        SELECT itemNo FROM Supplies_Non-Surgical WHERE itemNo = :NEW.ITEMNO
    );
    IF v_count = 0 THEN
        RAISE_APPLICATION_ERROR(-20001, 'ItemNo ' || :NEW.ITEMNO || ' 不存在于任何供应表中');
    END IF;
END;
/

表的创建方式和方案1一致,不需要额外添加CHECK约束。

方案3:重构表结构(更符合关系模型规范)

从长期维护的角度来看,更推荐的方式是创建一个统一的Supplies主表,让三个供应表作为子表关联主表:

步骤1:创建主Supplies表

CREATE TABLE Supplies (
    ITEMNO CHAR(6) PRIMARY KEY,
    SUPPLY_TYPE VARCHAR2(20) NOT NULL CHECK (SUPPLY_TYPE IN ('PHARMACEUTICAL', 'SURGICAL', 'NON-SURGICAL')),
    -- 在这里添加所有供应品的通用字段
    ...
);

步骤2:创建子表并关联主表

-- 药品供应子表
CREATE TABLE Supplies_Pharmaceutical (
    ITEMNO CHAR(6) PRIMARY KEY REFERENCES Supplies(ITEMNO),
    -- 添加药品专属字段
    ...
);

-- 手术用品子表
CREATE TABLE Supplies_Surgical (
    ITEMNO CHAR(6) PRIMARY KEY REFERENCES Supplies(ITEMNO),
    -- 添加手术用品专属字段
    ...
);

-- 非手术用品子表
CREATE TABLE Supplies_Non-Surgical (
    ITEMNO CHAR(6) PRIMARY KEY REFERENCES Supplies(ITEMNO),
    -- 添加非手术用品专属字段
    ...
);

步骤3:创建Requisition表

现在只需要让itemNo关联主Supplies表即可,天然实现了OR关系(因为所有有效itemNo都在主表中):

CREATE TABLE REQUISITION (
    REQNO CHAR(6) CONSTRAINT REQNO_PK PRIMARY KEY,
    STAFFNO CHAR(6) CONSTRAINT REQ_STAFFNO_FK REFERENCES STAFF_CHARGENURSE(STAFFNO),
    STAFFNAME VARCHAR2(100),
    WARDNO CHAR(6),
    ITEMNO CHAR(6) NOT NULL CONSTRAINT REQ_ITEMNO_FK REFERENCES Supplies(ITEMNO),
    QUANTITY INT,
    DATEORDERED DATE,
    DATERECEIVED DATE
);

这种方式更符合数据库设计的规范化原则,避免了复杂的约束或触发器,后续维护也会更简单。

内容的提问来源于stack exchange,提问作者Mark McGown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:38:58