【问题标题】:PL/SQL procedure to insert id to rows having same information将 id 插入具有相同信息的行的 PL/SQL 过程
【发布时间】:2017-01-15 14:00:04
【问题描述】:

我在 Oracle 表中有联系方式数据,如下所示...

我想插入新列以将相同的 ID 分配给具有匹配信息的联系人,即基于姓氏、名字和(电话和/或电子邮件)的组

输出应该如下所示

我是这个论坛的新手,所以在发布问题时遇到格式问题,请查看附件图片以便于理解我的要求

在我们庞大的数据库中寻找 PL/SQL 过程来完成这项工作

【问题讨论】:

  • 你能在这里分享示例数据,以便我可以尝试在本地查询。它真的很有帮助
  • 为什么你认为你需要一个存储过程?一个简单的rank() 就可以了
  • 始终将表格格式的示例数据作为文本而不是图像发布
  • @a_horse_with_no_name 如果您认为 rank() 可以解决我的目的,请提供解决方案。我正在考虑程序,因为数据很复杂,例如,您在我的 ID 2 示例输出要求中看到,电话和电子邮件有多种组合。对于相同的总共 13 个名字姓氏,这些实际上只是 4 个不同的联系人,因为您可以分析给定示例输出中的组合。

标签: sql oracle plsql


【解决方案1】:

由于您提供的数据的性质,我认为不可能在一个 SQL 中执行此操作或对其进行更好的调整。

创建表格并使用您的示例填充的脚本:

create table TAB_TEST(LAST_NAME VARCHAR2(200),
FIRST_NAME VARCHAR2(200),
PHONE NUMBER,
EMAIL VARCHAR2(200))
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 2058371579, 'ABC@GMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 4479940000, 'ABC@GMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7195739945, 'XYZ@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7475393956, 'XYZ@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7475393956, 'XYZ@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7473430336, null)
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7195739945, 'XYZ@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7475393956, '123@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', null, '123@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 1168548666, '456@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 1168548666, '456@HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 1168548666, null)
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7473430336, null)
/
ALTER TABLE TAB_TEST ADD ID NUMBER
/

以及要更新的脚本

DECLARE 
lncount   NUMBER := 1; 

-- had to create this record in order to add the rowid
TYPE lttab_test IS RECORD( 
  ROWID VARCHAR2(200), 
  last_name VARCHAR2(200), 
  first_name VARCHAR2(200), 
  phone NUMBER, 
  email VARCHAR2(200), 
  id NUMBER); 

TYPE lttype 
  IS TABLE OF LTTAB_TEST; 

larecords LTTYPE; 
BEGIN 
SELECT ROWID, 
       last_name, 
       first_name, 
       phone, 
       email, 
       id 
bulk   collect INTO larecords 
FROM   tab_test a; 


FOR i IN 1..larecords.count LOOP 

    IF Larecords(i).id IS NULL THEN 
      FOR j IN 1..i-1 LOOP 
          IF Larecords(j).phone = Larecords(i).phone
             AND Larecords(i).id IS NULL THEN 
            Larecords(i).id := Larecords(j).id; 

            exit; 
          END IF; 
      END LOOP; 

      FOR j IN 1..i-1 LOOP 
          IF Larecords(j).email = Larecords(i).email 
             AND Larecords(i).id IS NULL THEN 
            Larecords(i).id := Larecords(j).id; 

            exit; 
          END IF; 
      END LOOP; 

      IF Larecords(i).id IS NULL THEN 
        Larecords(i).id := lncount; 

        lncount := lncount + 1; 
      END IF; 

    END IF; 
END LOOP; 

forall i IN 1..larecords.count 
  UPDATE tab_test 
  SET    id = Larecords(i).id 
  WHERE  ROWID = Larecords(i).ROWID; 

COMMIT:
END; 

【讨论】:

  • 如果您可以将我的评论标记为答案,它可以在未来对其他人有所帮助,并且我会获得声誉。谢谢
  • 您上面的代码在样本测试数据上运行良好,但是当我们处理大量数据时它会失败。您能否分享一下批量限制的代码,比如测试数据中一次 5 行,这应该会产生相同的输出。
  • 'fors' 让它变慢了,说实话,我不知道如何将您的问题实施到更快的查询中。 Bulk 不起作用,因为它总是需要查看记录的先前状态。
【解决方案2】:

这不是最有效/最漂亮的代码,但它可以在您只需要序列号的情况下工作

DECLARE 
I NUMBER := 0;

BEGIN 
  ALTER TABLE TABLE 
  ADD ID NUMBER;
  COMMIT;

FOR REC IN (SELECT * FROM TABLE) 
  LOOP 
    IF(REC.ID IS NULL) THEN
      UPDATE TABLE T SET ID = I 
      WHERE (T.EMAIL = REC.EMAIL OR T.PHONE = REC.PHONE)
      AND ID IS NULL;
      COMMIT;
      I := I + 1;
    END IF;
  END LOOP;
END;

【讨论】:

  • DDL 自动提交事务,不需要使用 COMMIT。
  • TheName@ 如果您从 java 启动 PL/SQL 过程,情况可能并非总是如此
  • 什么意思?
  • TheName@在Java中创建连接对象,使用Connection.setAutoCommit设置autocommit为false,然后使用Java Connection.prepareCall调用存储过程
猜你喜欢
  • 1970-01-01
  • 2019-11-28
  • 2020-09-20
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
  • 1970-01-01
  • 2017-10-18
  • 2019-12-10
相关资源
最近更新 更多