你会如何在SQL脚本中强制执行DRY(不要重复自己)?

时间:2009-03-26 12:16:05

标签: sql oracle dry

我正在使用包含一些更新的脚本来更改数据库(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", ...}),然后在其上迭代单个更新查询。

谢谢!

8 个答案:

答案 0 :(得分: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');

答案 1 :(得分: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;

答案 2 :(得分: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');

答案 3 :(得分: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

答案 4 :(得分: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中匹配外卡的单个字符...

答案 5 :(得分:0)

根据脚本的重要程度,我会:

  1. 只需复制,粘贴和修改,或
  2. 使用其他编程语言编写脚本,以更好的方法解决重复问题。
  3. 对于替换规则,您可以创建一个临时表并使用这些替换规则填充它,然后加入此表。

    如果子查询始终相同,那么您也可以通过使用连接来解决第一个问题。

答案 6 :(得分:0)

我已经看到了一些方法:

  1. 使用字符串缓冲区使用PL / SQL或编程语言动态组装sql。
  2. 使用IBATIS等框架,让您重用并扩展存储在XML文件中的SQL片段。
  3. 使用ORM框架通过使用对象而不是直接使用SQL来解决此问题。
  4. 根据您的语言和使用框架的问题可能是最好的方法,然后将其扩展到您希望它做的事情。

答案 7 :(得分:0)

soulmerge建议的解决方案是最简单的,因此也是最好的解决方案 - 您只需要将调用嵌套到“替换”。我只想补充条件

status like '%tagada%'

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