【问题标题】:Inserting into a 1:1 reletationship插入 1:1 关系
【发布时间】:2019-06-06 04:33:27
【问题描述】:

我有两张桌子。表 A 和 B,每个都有一个自动递增的主键,并且外键不能为空。两个表都是 1:1 的关系。

因此,对于 A 中的每条记录,都必须有一个 B。

我无法工作的是插入此类表格。我得到的错误是“找不到父键”。

我是 Oracle 新手,感谢您的帮助。

这是 ER 的屏幕:

这是我的插入:

INSERT ALL
INTO NASTAVENI_TARIFU (ZADANI_D, ID_OBJEDNAVKY, ID_DODATKU, ID_TARIF, ID_NABIDKY) VALUES (TO_DATE('2018-01-01', 'yyyy-mm-dd' ), 1, SQ_SMLUVNI_DODATEK.nextval, 2, NULL)
INTO SMLUVNI_DODATKY (PROLONGACE_D,ID_NASTAVENI_TARIFU) VALUES (TO_DATE('2019-12-22', 'yyyy-mm-dd' ), SQ_NASTAVENI_TARIFU.currval)
SELECT * FROM DUAL

以下是定义:

CREATE TABLE nastaveni_tarifu (
    id_nastaveni_tarifu   INTEGER NOT NULL,
    zadani_d              DATE NOT NULL,
    id_objednavky         INTEGER NOT NULL,
    id_dodatku            INTEGER NOT NULL,
    id_tarif              INTEGER NOT NULL,
    id_nabidky            INTEGER
);

CREATE TABLE smluvni_dodatky (
    id_dodatku            INTEGER NOT NULL,
    prolongace_d          DATE NOT NULL,
    id_nastaveni_tarifu   INTEGER NOT NULL
);

ALTER TABLE smluvni_dodatky
    ADD CONSTRAINT fk_dodatek_nast_tarifu FOREIGN KEY ( id_nastaveni_tarifu )
        REFERENCES nastaveni_tarifu ( id_nastaveni_tarifu );

ALTER TABLE nastaveni_tarifu
    ADD CONSTRAINT fk_nast_tarifu_dodatek FOREIGN KEY ( id_dodatku )
        REFERENCES smluvni_dodatky ( id_dodatku );

CREATE SEQUENCE sq_nastaveni_tarifu START WITH 1 MINVALUE 1 NOCACHE ORDER;

CREATE OR REPLACE TRIGGER tr$ntpreinsert BEFORE
    INSERT ON nastaveni_tarifu
    FOR EACH ROW
    WHEN ( new.id_nastaveni_tarifu IS NULL )
BEGIN
    :new.id_nastaveni_tarifu := sq_nastaveni_tarifu.nextval;
END;

CREATE SEQUENCE sq_smluvni_dodatek START WITH 1 MINVALUE 1 NOCACHE ORDER;

CREATE OR REPLACE TRIGGER tr$sdpreinsert BEFORE
    INSERT ON smluvni_dodatky
    FOR EACH ROW
    WHEN ( new.id_dodatku IS NULL )
BEGIN
    :new.id_dodatku := sq_smluvni_dodatek.nextval;
END;

...我开始讨厌 Oracle,这不应该那么难:(

【问题讨论】:

  • “我无法工作” 没有帮助。您应该告诉我们您在插入时收到的确切错误消息。此外,您应该将表定义添加为 纯文本,而不是使用图像或 ER 图。
  • 您有一个循环外键引用,这是一个糟糕的数据库设计。一种解决方法是在创建约束时使用deferrable。但是,推荐的解决方案是重新审视设计并提出一个更好的设计,而不需要循环引用。
  • 我不明白...期望的结果是每个 A 都必须有一个 B。这就是客户的需求,我想要的只是执行该操作的外键。有没有办法像在事务中那样将这两条记录插入到 oracle 中?
  • 是的,正如@KaushikNayak 所说,为什么不将所有数据放在一张表中?将其拆分为 2 的原因是什么?

标签: oracle sql-insert


【解决方案1】:

正如我所提到的,这是一个糟糕的设计,因为两个表之间存在循环引用。

您的insert all 语句的问题在于,第二个into 子句无法看到SQ_NASTAVENI_TARIFU.nextval,因为它只能在整个语句完成后才能初始化。

即使您将其转换为 2 个插入语句,它也不起作用,因为当值在每种情况下都依赖于其他值并且还声明了外键列时,您无法确保在一个表中有一行not null.

要么将它们定义为NULL,然后尝试先插入空值然后更新它,要么仅将Deferred Constraints 用作解决方法

ALTER TABLE smluvni_dodatky
    ADD CONSTRAINT fk_dodatek_nast_tarifu FOREIGN KEY ( id_nastaveni_tarifu )
        REFERENCES nastaveni_tarifu ( id_nastaveni_tarifu ) 
    deferrable initially deferred;


ALTER TABLE nastaveni_tarifu
    ADD CONSTRAINT fk_nast_tarifu_dodatek FOREIGN KEY ( id_dodatku )
        REFERENCES smluvni_dodatky ( id_dodatku ) deferrable initially deferred;

现在,您的第二个触发器还有另一个问题,除非使用序列 sq_smluvni_dodatek 生成的表 smluvni_dodatky 的主键可以存储插入到 nastaveni_tarifu 中的相同值,否则无法保持恒定性.

因此在您的触发器tr$sdpreinsert 中,将赋值表达式更改为此

:new.id_dodatku := sq_smluvni_dodatek.currval;

现在,像这样将两个插入作为单独的语句运行会起作用,因为在发出 commit 之前不会进行约束检查。

  INSERT   INTO nastaveni_tarifu (
          zadani_d,id_objednavky,id_dodatku,id_tarif,id_nabidky
     ) VALUES (
          TO_DATE('2018-01-01','yyyy-mm-dd'), 1,sq_smluvni_dodatek.NEXTVAL, 2,NULL
     ) ;
 INSERT INTO smluvni_dodatky (
          prolongace_d,id_nastaveni_tarifu
     ) VALUES (
          TO_DATE('2019-12-22','yyyy-mm-dd'),sq_nastaveni_tarifu.currval);
commit;

DEMO

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多