将 id 插入具有相同信息的行的 PL/SQL 过程

Posted

技术标签:

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

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

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

输出应该如下所示

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

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

【问题讨论】:

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

以上是关于将 id 插入具有相同信息的行的 PL/SQL 过程的主要内容,如果未能解决你的问题,请参考以下文章

将 SQL 数据插入具有相同 ID 的行中?

如何查询仅出现特定列中具有最高值的行的行?

选择可能具有相同值的行的多个实例

计算具有3个列值的行的出现次数相同的MySQL

如何在具有某些首字母的新表中插入所有行(pl/sql)

PL / SQL触发器在更新或插入后更新同一个表