4

我正在使用包含一些更新的脚本更改数据库(oracle),如下所示:

UPDATE customer
SET status = REPLACE(status,   'X_Y',   'xy')
WHERE status LIKE '%X_Y%'
 AND category_id IN
  (SELECT id
   FROM category
   WHERE code = 'ABC');

UPDATE customer
SET status = REPLACE(status,   'X_Z',   'xz')
WHERE status LIKE '%X_Z%'
 AND category_id IN
  (SELECT id
   FROM category
   WHERE code = 'ABC');

-- More updates looking the same...

在这种情况下,您将如何执行 DRY(不要重复自己)?

我对解决以下两个反复出现的问题特别感兴趣:

  • 定义一个仅可从此脚本中获得的函数以提取子查询SELECT id FROM category WHERE code = 'ABC'

  • 创建一组替换规则(可能类似于{"X_Y": "yx", "X_Z": "xz", ...}流行的编程语言),然后在其上迭代单个更新查询。

谢谢!

4

8 回答 8

5

我会将其简化为单个查询:

UPDATE customer
SET status = REPLACE(REPLACE(status, 'X_Y', 'xy'), 'X_Z', 'xz')
WHERE status REGEXP_LIKE 'X_[YZ]'
 AND category_id IN
  (SELECT id
   FROM category
   WHERE code = 'ABC');
于 2009-03-26T12:42:50.827 回答
2

首先,请记住,脚本与编程不是一回事,您不必遵守DRY 原则。像这样的脚本通常是一次性的,而不是要长期维护的程序。

但是您可以使用 PL/SQL 来执行此操作:

declare
   type str_tab is table of varchar2(30) index by binary_integer;
   from_tab str_tab;
   to_tab str_tab;
begin
   from_tab(1) := 'X_Y';
   from_tab(2) := 'X_Z';
   to_tab(1) := 'xy';
   to_tab(2) := 'xz';

   for i in 1..from_tab.count loop

      UPDATE customer
      SET status = REPLACE(status,   from_tab(i),   to_tab(i))
      WHERE status LIKE '%' || from_tab(i) || '%'
       AND category_id IN
        (SELECT id
         FROM category
         WHERE code = 'ABC');

   end loop;
end;
于 2009-03-26T12:32:43.330 回答
2

非常简单,除非我遗漏了什么。

UPDATE customer
SET status = REPLACE(REPLACE(status,'X_Y','xy'),'X_Z','xz')
WHERE ( status LIKE '%X_Y%' Or status LIKE '%X_Z%')
  AND category_id IN
     (SELECT id
      FROM category
      WHERE code = 'ABC');
于 2009-03-26T13:06:49.037 回答
1

编写一个带参数的脚本并多次调用它。(我假设您使用 SQLPlus 来运行脚本。)

replace_in_status.sql:

UPDATE customer
SET status = REPLACE(status,   UPPER('&1'),   '&2')
WHERE status LIKE '%' ||UPPER('&1')|| '%'
 AND category_id IN
  (SELECT id
   FROM category
   WHERE code = 'ABC');

调用脚本:

@replace_in_status X_Y xy
@replace_in_status X_Z xz
于 2009-03-26T12:44:16.657 回答
1

好的,在这里从臀部射击,放轻松我的语法:-)

这样的方法是否有帮助:

DECLARE
  v_sql1   VARCHAR2(1000);
  v_sql2   VARCHAR2(2000);
  TYPE T_Rules IS RECORD (srch  VARCHAR2(100),  repl(VARCHAR2(100));
  TYPE T_RuleTab IS TABLE OF T_Rules INDEX BY BINARY_INTEGER;
  v_rules T_RuleTab;

  FUNCTION get_subquery RETURN VARCHAR2 IS
  BEGIN
    RETURN '(SELECT id FROM category WHERE code = ''ABC'')';
  END;

BEGIN
  v_sql1 := 'UPDATE customer SET status = REPLACE('':1'','':2'') WHERE status LIKE ''%:1%'' AND category_id IN ';
  v_rules(1).srch := ('X_Y'); v_rules(1).repl := 'yx';
  v_rules(2).srch := ('X_Z'); v_rules(2).repl := 'xz';

  FOR i IN 1..v_rules.COUNT LOOP
    v_sql2 := v_sql1||get_subquery();
    EXECUTE IMMEDIATE v_sql2 USING v_rules(i).srch, v_rules(i).repl;
  END LOOP;
END;

您可以将 PL/SQL 表替换为真实表并在其上运行光标,但这满足了您的第二个要求。

显然,在 get_subquery 上还有一些工作要做,这是您的第一个要求;-)

编辑

当!忘了提到您需要小心 WHERE 子句中的替换字符串 - 下划线是 Oracle 中与通配符匹配的单个字符...

于 2009-03-26T15:57:16.837 回答
0

soulmerge 建议的解决方案是最简单的,因此也是最好的解决方案 - 您只需嵌套调用“替换”即可。我只想添加条件

status like '%tagada%'

没用。如果未找到搜索的字符串,replace() 不会改变状态,因此您可以安全地将其应用于所有行。而且由于您搜索丢失在另一个字符串中间的字符串的条件不能使用您拥有的任何索引,因此它作为过滤条件是无用的。您唯一的过滤条件是 category_id 上的条件......这带来了一点证明为什么 Soulmerge 的解决方案是最好的:迭代所有更改是一个坏主意。假设 category_id 上的过滤器具有中等选择性,Oracle 很可能会选择扫描表。当您可以一次完成所有更改时,您真的要每次都扫描表吗?

于 2009-03-26T15:29:07.543 回答
0

我已经看到了一些方法:

  1. 使用字符串缓冲区使用 PL/SQL 或在您的编程语言中动态组装 sql。
  2. 使用 IBATIS 之类的框架,它可以让您重用和扩展存储在 XML 文件中的 SQL 片段。
  3. 使用 ORM 框架通过使用对象而不是直接使用 SQL 来规避这个问题。

根据您的语言和手头的问题,使用框架可能是最好的方法,然后将其扩展为您想要它做的事情。

于 2009-03-26T12:36:31.137 回答
0

根据脚本的重要性,我会:

  1. 只需复制粘贴和修改,或
  2. 用另一种编程语言编写脚本,以更好地解决重复问题。

对于替换规则,您可以创建一个临时表并用这些替换规则填充它,然后与该表连接。

如果子查询始终相同,则您也通过使用连接解决了第一个问题。

于 2009-03-26T12:32:37.327 回答