将表更新重写为set而不是迭代方法

时间:2016-09-26 19:49:58

标签: oracle plsql

我在测试Oracle数据库中有一个用户电子邮件地址列表,它们当前都设置为相同的值。我想用唯一的条目替换它们,其中包含许多无效的地址和空值。我的表目前看起来像这样,总共大约250k行。(我已经排除了空和无效的条目来保存一些空间)

+-------------+--------------------+
| employee_id |       email        |
+-------------+--------------------+
|           1 | test@testemail.com |
|           2 | test@testemail.com |
|           3 | test@testemail.com |
|...          |...                 |
+-------------+--------------------+

我希望它看起来像这样

+-------------+---------------------+
| employee_id |        email        |
+-------------+---------------------+
|           1 | test1@testemail.com |
|           2 | test2@testemail.com |
|           3 | test3@testemail.com |
|...          |...                  |
+-------------+---------------------+

我已经编写了以下PL / SQL来进行更改,它可以正常工作,但它似乎非常低效。我可以使用其他方法来利用集合处理吗?

感谢您提供任何帮助。

DECLARE
    i number(20);
    l_employee_id hr.employees.employee_id%TYPE;
    output_query varchar2(1000);
    CURSOR c_cursor IS
        SELECT employee_id
        FROM hr.employees;
PROCEDURE update_sql(id_num IN hr.employees.employee_id%TYPE, 
                     email IN VARCHAR2) IS
BEGIN
    output_query := 'UPDATE hr.employees
                    SET email = '''|| email ||'''
                    WHERE employee_id = '|| id_num;
                    dbms_output.put_line(output_query); --for debug
    EXECUTE IMMEDIATE output_query;
END;
BEGIN
    OPEN c_cursor;
    i := 1;
    <<outer_loop>>
    LOOP
        For j IN 1..5 LOOP
            FETCH c_cursor INTO l_employee_id;
            EXIT outer_loop WHEN c_cursor%NOTFOUND;
            IF j <= 3 THEN
                update_sql(l_employee_id, ('test' || i || '@testemail.com'));
            ELSIF j = 4 THEN
                update_sql(l_employee_id, ('test' || i || 'testemail.com'));
            ELSIF j = 5 THEN
                update_sql(l_employee_id, ' ');
            END IF;
            i := i + 1;
        END LOOP;
    END LOOP outer_loop;
    CLOSE c_cursor;
END;
/

编辑 - 2016年9月26日 - 澄清表的大小。

3 个答案:

答案 0 :(得分:4)

 create table emp (emp_id number, email varchar2(32));

 insert into emp select level as emp_id, 'test@testemail.com' as email 
 from dual connect by level<=2500000;

 update emp set email = regexp_replace(email, '(\w+)(@\w+\.\w+)', '\1' || emp_id || '\2');
 --250,000 rows updated ~16 sec.

EMP_ID, EMAIL
1   test1@testemail.com
2   test2@testemail.com
3   test3@testemail.com
...

drop table emp;

答案 1 :(得分:2)

首先,即使你要迭代编码,请不要在没有必要的情况下使用动态SQL。只有在编译时不知道要查询的表或列时才有必要。

那就是说,听起来你只是想要

UPDATE employees
   SET email = (case when employee_id <= 3
                     then 'test' || employee_id || '@testemail.com'
                     when employee_id = 4
                     then 'test' || employee_id || 'testemail.com'
                     when employee_id = 5
                     then ' '
                     else null
                  end)

答案 2 :(得分:0)

Oracle安装程序

create table employees (
  id    NUMBER,
  email VARCHAR2(100)
);

INSERT INTO employees
SELECT 1, 'test@testemail.com' FROM DUAL UNION ALL
SELECT 2, 'test@testemail.com' FROM DUAL UNION ALL
SELECT 3, 'test@testemail.com' FROM DUAL UNION ALL
SELECT 4, 'test@testemail.com' FROM DUAL UNION ALL
SELECT 5, 'test@testemail.com' FROM DUAL UNION ALL
SELECT 6, 'test@testemail.com' FROM DUAL;

<强>查询

update employees
set email = CASE MOD( ROWNUM, 5 )
            WHEN 4 THEN 'test' || ROWNUM || 'testemail.com'
            WHEN 0 THEN ''
                   ELSE 'test' || ROWNUM || '@testemail.com'
            END;

<强>输出

SELECT * FROM employees;

        ID EMAIL
---------- ------------------------
         1 test1@testemail.com
         2 test2@testemail.com
         3 test3@testemail.com
         4 test4testemail.com
         5 
         6 test6@testemail.com