【问题标题】:How to insert data into table with composite unique keys如何使用复合唯一键将数据插入表中
【发布时间】:2022-08-20 19:10:11
【问题描述】:

我有一个表格演示,其中有 5 列,如下所示:

Create table demo
   
   ( 
   Demo_id number,
   demo_a number,
   Demo_b number,
   Demo_c number
  )

所以这里 demo_id 是代理 PK 和 demo_a,demo_b,demo_cis 的组合唯一

所以我创建了一个这样的SP

save 
 (  pdemo_a in number. ,
     p_demo_b in number. , 
     p_demp_c in number,
     p_demo_id out number
 )

所以我从 API 接收数据到这个 SP,我必须将数据插入到我的 demo_table 中。所以我写了逻辑,我正在检查表中是否存在 (demo_a,demo_b,demo_c) 的组合。如果它存在,那么我将 demo_id 选择到一个变量中并将其返回给 API,否则我将使用 no_data_found 异常将这个唯一组合的数据插入我的表中。

所以基本上代码是:

Begin

    Select demo_id 
      into p_demo_id 

      from demo 
        where demo _a=p_demo_a
        and demo_b=p_demo_b
        and demo_c = p_demo_c;

Exception 

    When no data found 
    then 

        insert into demo values()

问题是作为唯一键一部分的列可以为空,因此在这种情况下,当我检查组合是否存在时,因为任何字段都可以为空,因此我的 select into 子句失败。并且代码转到 no_data_found 部分并尝试将记录插入表中,但它失败了,因为``该组合存在于表中。所以我该怎么做。如何在不引发错误的情况下将 demo_id 返回到 API。

  • 检查它是否已经存在的查询在哪里?您也可以改进它以检查空值。
  • 您也可以在没有预先检查的情况下使用插入并处理重复键错误。它还使用较少的表访问

标签: sql oracle plsql insert-update


【解决方案1】:

你可以写出你在这种情况下的实际情况:

(demo_a=p_demo_a OR (demo_a is null and p_demo_a is null)) AND ...

但是使用decode 会更简单。就像是:

where decode(demo_a, p_demo_a, 1, 0) = 1
and decode(demo_b, p_demo_b, 1, 0) = 1
and decode(demo_c, p_demo_c, 1, 0) = 1

decode(demo_a, p_demo_a, 1, 0) 仅表示“如果 demo_a = p_demo_a,则返回 1,否则返回 0”,但它会按照您期望的方式进行空比较(null==null 为真)。

编辑添加:我认为上面的答案解决了您的具体问题,但您可能应该重新考虑该过程。如 cmets 中所述,通常更好的策略可能是尝试插入并在失败时捕获错误。这将更好地支持并发性(如果您的过程的多个副本同时运行)并且可能性能更高。

【讨论】:

    【解决方案2】:

    如果我做对了,您需要检查演示表中的 A、B、C 列中是否存在参数 A、B、C(从 API 接收)的现有组合。参数中可能存在 NULL 值。如果没有这样的组合,您必须插入一条记录。我不知道你如何生成你的 PK(列 DEMO_ID),所以我发明了我自己的。该主键列可以是任何东西。 首先,我创建了包含几行的演示表:

    --     DEMO_ID     DEMO_A     DEMO_B     DEMO_C
    --  ---------- ---------- ---------- ----------
    --      100123          1          2          3 
    --      100023          2                     3 
    --      100132          1          3          2 
    --      100456          4          5          6 
    --      100567          5          6          7 
    --      100089          8                     9 
    --      100293          2          9          3
    

    Next - 一个过程 SAVE 来检查是否存在,如果不存在则插入记录。

    create or replace PROCEDURE SAVE 
         (    p_demo_a in number,
              p_demo_b in number, 
              p_demo_c in number,
              p_demo_id out number
         ) AS
         Status    VarChar2(200);
    BEGIN
        Declare 
        
        Begin
            Select  DEMO_ID 
            Into    p_demo_id 
            From    DEMO 
            Where   Nvl(DEMO_A, 999999999) = Nvl(p_demo_a, 999999999) And 
                    Nvl(DEMO_B, 999999999) = Nvl(p_demo_b, 999999999) And 
                    Nvl(DEMO_C, 999999999) = Nvl(p_demo_c, 999999999);
             Status := 'OK - NO INSERT - RECORD EXISTS --> DEMO_ID = ' || p_demo_id;
        Exception
            WHEN NO_DATA_FOUND THEN
                Begin
                    p_demo_id := 100000 + Nvl(To_Number(To_Char(p_demo_a) || To_Char(p_demo_b) || To_Char(p_demo_c)), 0);
                    INSERT INTO DEMO (DEMO_ID, DEMO_A, DEMO_B, DEMO_C) 
                        VALUES(p_demo_id, p_demo_a, p_demo_b, p_demo_c);
                    Commit;
                    Status := 'OK - RECORD INSERTED --> DEMO_ID = ' || p_demo_id;
                Exception
                    WHEN DUP_VAL_ON_INDEX THEN
                        p_demo_id := 0;
                        Status := 'ERR (SAVE) INNER BLOCK - DUP_VAL_ON_INDEX --> DEMO_ID = ' || p_demo_id;
                    WHEN OTHERS THEN
                        p_demo_id := 0;
                        Status := 'ERR (SAVE) INNER BLOCK - OTHERS  --> ' || SQLERRM;
                End;
            WHEN OTHERS THEN
                p_demo_id := 0;
                Status := 'ERR (SAVE) OUTER BLOCK - OTHERS  --> ' || SQLERRM;
        End;
        DBMS_OUTPUT.PUT_LINE(Status);
    END SAVE;
    

    测试 1。

    SET SERVEROUTPUT ON
    Declare
        p_id    NUMBER := Null;
    Begin
        SAVE(7, 6, 9, p_id);
        If Nvl(p_id, 0) = 0 Then
            DBMS_OUTPUT.PUT_LINE('Something went wrong - check the status');
        End If;
    End;
    --
    --  R e s u l t
    --
    --  anonymous block completed
    --  OK - RECORD INSERTED --> DEMO_ID = 100769
    

    测试 2. 具有空值

    SET SERVEROUTPUT ON
    Declare
        p_id    NUMBER := Null;
    Begin
        SAVE(7, Null, 9, p_id);
        If Nvl(p_id, 0) = 0 Then
            DBMS_OUTPUT.PUT_LINE('Something went wrong - check the status');
        End If;
    End;
    --
    --  R e s u l t
    --
    --  anonymous block completed
    --  OK - RECORD INSERTED --> DEMO_ID = 100079
    

    如果你用相同的参数再试一次,结果是:

    --
    --  R e s u l t
    --
    --  anonymous block completed
    --  OK - NO INSERT - RECORD EXISTS --> DEMO_ID = 100079
    

    使用我的 PK 定义系统,如果您将参数 A、B、C 传递为 Null、8、9 - 由于重复键会出现错误。

    --
    --  R e s u l t
    --
    --  anonymous block completed
    --  ERR (SAVE) INNER BLOCK - DUP_VAL_ON_INDEX --> DEMO_ID = 0
    --  Something went wrong - check the status
    

    但是,如前所述 - 我不知道您如何生成这些代理键。在这种情况下,该键的 A、B、C 组合为 8、null、9 的记录与 null、8、9 不同,但我故意创建了相同的 PK 以显示可能的问题。如果在生成 PK 时有一个独特的数字代替空值(或者,甚至更好的是,一个序列号),那么这里将是一个新的 PK 并且将插入记录。因此,检查空值和插入应该不是问题,但 PK 生成可以。 问候...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-21
      • 1970-01-01
      • 2016-11-23
      • 2011-12-24
      相关资源
      最近更新 更多