如何抽象出子查询?

时间:2011-08-11 17:55:34

标签: sql oracle plsql

我有一个查询需要检查所有字段的值是否在有效代码列表中。现在我一遍又一遍地调用相同的子查询。我想将子查询抽象出来,以便更快,代码不会重复。这是有问题的查询:

select count(*)
into cnt
from pdv_validcodes c
where c.code_type = 'YNNA'
 and (upper(:new.spec_1) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_1 is null)
 and (upper(:new.spec_2) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_2 is null)
 and (upper(:new.spec_3) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_3 is null)
 and (upper(:new.spec_4) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_4 is null)
 and (upper(:new.spec_5) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_5 is null)
 and (upper(:new.spec_6) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_6 is null)
 and (upper(:new.spec_7) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_7 is null)
 and (upper(:new.spec_8) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_8 is null)
 and (upper(:new.spec_9) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_9 is null)
 and (upper(:new.spec_10) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.spec_10 is null)
 and (upper(:new.add_spec_1) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.add_spec_1 is null)
 and (upper(:new.add_spec_2) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.add_spec_2 is null)
 and (upper(:new.add_spec_3) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.add_spec_3 is null)
 and (upper(:new.add_spec_4) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.add_spec_4 is null)
 and (upper(:new.add_spec_5) in
     (select code from pdv_validcodes where code_type = 'YNNA') or
     :new.add_spec_5 is null);

6 个答案:

答案 0 :(得分:3)

迈克尔,

我没有机会测试这个,但因为它是触发器代码,因此PL / SQL,这可能有用:

CREATE OR REPLACE TYPE "strarray" AS TABLE OF VARCHAR2 (255)
/


DECLARE
   validcodes strarray;
BEGIN
   SELECT code 
     BULK COLLECT INTO validcodes
     FROM pdv_validcodes
    WHERE code_type = 'YNNA'
   UNION
   SELECT 'NULL'
     FROM dual;

   IF  NVL(upper(:new.spec_1), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_2), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_3), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_4), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_5), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_6), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_7), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_8), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_9), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.spec_10), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.add_spec_1), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.add_spec_2), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.add_spec_3), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.add_spec_4), 'NULL') MEMBER OF validcodes
   AND NVL(upper(:new.add_spec_5), 'NULL') MEMBER OF validcodes
   THEN
      -- Business logic
   ELSE
      -- Business logic
   END IF;
END;

答案 1 :(得分:2)

根据Oracle版本,您可以使用WITH子句来分解子查询。我不确定在这种情况下你买得太多,但是

with valid as (
  select code
    from pdv_validcodes
   where code_type = 'YNNA' )
select count(*)
into cnt
from pdv_validcodes c
where c.code_type = 'YNNA'
 and (upper(:new.spec_1) in
     (select * from valid) or
     :new.spec_1 is null)
 and (upper(:new.spec_2) in
     (select * from valid) or
     :new.spec_2 is null)
 and (upper(:new.spec_3) in
     (select * from valid) or
     :new.spec_3 is null)
...

答案 2 :(得分:1)

您可以将代码封装在VIEW中。这对性能没有帮助,但它有助于提高可读性和不正确剪切的错误,并且应该提高可维护性。

最好的选择是重新设计该表。

答案 3 :(得分:1)

我建议采用稍微不同的方法解决问题:将 :new. 值列表作为结果集实现。这样你就可以像对待桌子一样处理它。

以下查询将返回 :new. 值的计数,这些值为非空且与pdv_validcodes表中的代码不匹配。


SELECT COUNT(1)
  INTO cnt
  FROM (
         SELECT q.spec
           FROM ( SELECT :new.spec_1 AS spec FROM DUAL
                  UNION ALL SELECT :new.spec_2 FROM DUAL
                  UNION ALL SELECT :new.spec_3 FROM DUAL
                  UNION ALL SELECT :new.spec_4 FROM DUAL
                  UNION ALL SELECT :new.spec_5 FROM DUAL
                  UNION ALL SELECT :new.spec_6 FROM DUAL
                  UNION ALL SELECT :new.spec_7 FROM DUAL
                  UNION ALL SELECT :new.spec_8 FROM DUAL
                  UNION ALL SELECT :new.spec_9 FROM DUAL
                  UNION ALL SELECT :new.spec_10 FROM DUAL
                  UNION ALL SELECT :new.add_spec_1 FROM DUAL
                  UNION ALL SELECT :new.add_spec_2 FROM DUAL
                  UNION ALL SELECT :new.add_spec_3 FROM DUAL
                  UNION ALL SELECT :new.add_spec_4 FROM DUAL
                  UNION ALL SELECT :new.add_spec_5 FROM DUAL
               ) q WHERE q.spec IS NOT NULL
       ) p
  LEFT
  JOIN pdv_validcodes c
    ON c.code = UPPER(p.spec) AND c.code_type = 'YNNA'
 WHERE c.code IS NULL

以下是它的工作原理:

首先,我们将 :new. 值列表作为结果集返回。 (这是内联视图别名为 q 。)

接下来,我们从该结果集中排除任何NULL值。 (这是内联视图别名为 p 。)

接下来,我们将该结果集与 pdv_validcodes 表一起加入。 (我们仅匹配 'YNNA' code_type,我们将匹配作为OUTER联接( LEFT JOIN ),以便我们返回所有行来自p结果集,无论它们是否与 pdv_validcodes 表中的代码匹配。

作为最后一步,我们会排除我们找到匹配项的所有行( c.code NULL 来自 p 没有匹配项,只留下没有匹配项的 :new. 值列表。


注意:

当所有:new时,此查询将返回零计数。值匹配,如果有任何:new,将返回非零计数。未找到匹配项的值(我认为与原始版本相反)

这可能不是执行操作的最佳方式,但它确实消除了原始查询中的大量冗余代码。

'YNNA'文字只指定一次,每个都是:new。表达式只指定一次。

我假设所有这些的数据类型:new。表达式是兼容的(例如,都是VARCHAR),因为我们注意到它们都与代码列进行了比较。如果不是,那么在原始查询中会进行一些隐式数据类型转换,这可能需要在此明确显示,以便UNION ALL操作可以正常工作。)

Common Table Expression可以替代(old-school)内联视图。

此代码尚未经过测试。

答案 4 :(得分:1)

  

检查所有字段的值是否在有效代码列表中

这些方面应该做的事情

select sys.dbms_debug_vc2coll( 
        :new.spec_1 , :new.spec_2 , :new.spec_3 , :new.spec_4 , :new.spec_5 ,
        :new.spec_6 , :new.spec_7 , :new.spec_8 , :new.spec_9 , :new.spec_10,
        :new.add_spec_1, :new.add_spec_2 , :new.add_spec_3 , :new.add_spec_4 )
       multiset except distinct
         (select cast(collect(code) as sys.dbms_debug_vc2coll)
          from pdv_validcodes where code_type = 'YNNA')
from dual;

我使用了sys.dbms_debug_vc2coll但您可以创建自己的集合类型[CREATE TYPE tab_char AS TABLE OF VARCHAR2(20)]

如果查询返回除一个空值之外的任何值,那么这些值就是不匹配的值。

就个人而言,我会考虑忽略检查,并确保对数据库有参考约束,只需使用DML错误记录来处理任何狡猾的值。

答案 5 :(得分:0)

您需要的关系运算符是division,通常称为"the supplier who supplies all parts"

需要考虑的事项:exact division or division with remainder?;如何处理一个空的分配(例如,如果供应的零件清单是空集,逻辑上所有供应商都可以提供它,但是对没有供应商进行评估更实际)。