具有查询的Null值的列

时间:2018-02-16 12:28:39

标签: sql oracle informix

我正在尝试将查询从Informix转换为Oracle。 在Informix中,此查询

SELECT  a.base_imp,
        b.num_exp,
        c.tip_canon_unit,
        b.cod_tip_ben,
        b.dat_cr_ini,
        b.dat_cr_fi,
        b.cod_emb,
        b.nif_Abo_liq,
        0 as usuari,
        '' as nom_usu,
        '' as acotacio 
FROM  OUTER dpt_mant_can_reg_3 a,
      dpt_mant_canreg_2 b,
      OUTER dpt_mant_can_reg_1 c
WHERE a.cod_emb=b.cod_emb and b.cod_emb=c.cod_embassament and 
      a.cod_tip_ben = b.cod_tip_ben 
      and b.cod_tip_ben = c.cod_tip_ben and a.nif_abo_liq=b.nif_abo_liq 
      and b.nif_abo_liq = '39203275M' and b.cod_emb = 1
and (
     (b.dat_cr_ini >= date('01/01/2018') and b.dat_cr_ini <= 
     date('31/12/2018')) 
     or (b.dat_cr_fi >= date('01/01/2018') and b.dat_cr_fi <= 
     date('31/12/2018') 
     or (b.dat_cr_ini <= date('01/01/2018') and b.dat_cr_fi >= 
     date('31/12/2018')) 
     and a.dat_cr_ini = date('01/01/2018') and c.dat_cr_ini = 
     date('01/01/2018')));

结果为19行,第一列中的所有数据(a.base_imp字段)都为NULL值。 之所以会发生这种情况,是因为WHERE子句(a.dat_cr_ini = date('01 / 01/2018'))没有巧合,但是Informix允许给出结果,将NULLS放在表中没有巧合的字段列中。

如果我尝试对Oracle执行相同的操作

SELECT a.base_imp,
   b.num_exp,
   c.tip_canon_unit,
   b.cod_tip_ben,
   b.dat_cr_ini,
   b.dat_cr_fi,
   b.cod_emb,
   b.nif_Abo_liq,
   0 as usuari,
   '' as nom_usu,
   '' as acotacio 
FROM  dpt_mant_can_reg_3 a LEFT OUTER JOIN dpt_mant_canreg_2 b ON 
   (a.cod_emb=b.cod_emb and a.cod_tip_ben = b.cod_tip_ben and 
    a.nif_abo_liq=b.nif_abo_liq)
   LEFT OUTER JOIN dpt_mant_can_reg_1 c ON 
   (b.cod_emb=c.cod_embassament and b.cod_tip_ben = c.cod_tip_ben ) 
WHERE 1=1 and b.nif_abo_liq = '39203275M' and b.cod_emb = 1 
   AND   ((b.dat_cr_ini >= to_date('01/01/2018','DD/MM/YYYY') and 
   b.dat_cr_ini <= to_date('31/12/2018','DD/MM/YYYY')) or 
   (b.dat_cr_fi >= to_date('01/01/2018','DD/MM/YYYY') and 
   b.dat_cr_fi <= to_date('31/12/2018','DD/MM/YYYY') or 
   (b.dat_cr_ini <= to_date('01/01/2018','DD/MM/YYYY') and 
   b.dat_cr_fi >= to_date('31/12/2018','DD/MM/YYYY')) 
   and a.dat_cr_ini = to_date('01/01/2018','DD/MM/YYYY') and 
   c.dat_cr_ini = to_date('01/01/2018','DD/MM/YYYY')))

没有结果(0行)如果没有巧合,Oracle不允许从此查询中检索数据。我如何修改查询以检索行的方式与Informix相同?

2 个答案:

答案 0 :(得分:0)

您需要将日期条件移至on子句。您还应该了解date关键字。

您的where条件会将外连接转换为内连接。通常,正确的解决方案是将条件移动到on子句。它看起来像这样:

FROM dpt_mant_can_reg_3 a LEFT OUTER JOIN
     dpt_mant_canreg_2 b
     ON a.cod_emb = b.cod_emb and
        a.cod_tip_ben = b.cod_tip_ben and 
        a.nif_abo_liq = b.nif_abo_liq and
        b.nif_abo_liq = '39203275M' and
        b.cod_emb = 1 and
        ( (b.dat_cr_ini >= date '2018-01-01' and
           b.dat_cr_ini <= date '2018-12-31'
          ) or
          (b.dat_cr_fi >= date '2018-01-01' and
           b.dat_cr_fi <= date '2018-12-31'
          )
        )
        . . . LEFT OUTER JOIN
     dpt_mant_can_reg_1 c
     ON b.cod_emb  = c.cod_embassament and
        b.cod_tip_ben = c.cod_tip_ben and
        c.dat_cr_ini = date '2018-01-01'
 WHERE a.dat_cr_ini = date '2018-01-01' 

我还建议你更改表别名。 abc毫无意义。使用表名称的缩写,例如cr3cr2cr1

答案 1 :(得分:0)

Informix样式的OUTER连接是一个有趣的结构,与标准或任何其他DBMS中的任何内容都不太相似。

您的原始查询具有FROM子句和主要连接条件,例如:

FROM  OUTER dpt_mant_can_reg_3 a,
      dpt_mant_canreg_2 b,
      OUTER dpt_mant_can_reg_1 c
WHERE a.cod_emb = b.cod_emb AND b.cod_emb = c.cod_embassament AND 
      a.cod_tip_ben = b.cod_tip_ben 
      AND b.cod_tip_ben = c.cod_tip_ben AND a.nif_abo_liq = b.nif_abo_liq 

“占主导地位”&#39; table有别名b,另外两个是外连接的。在您提议的翻译中,您有:

FROM dpt_mant_can_reg_3 a
LEFT OUTER JOIN dpt_mant_canreg_2 b ON 
     (a.cod_emb = b.cod_emb and a.cod_tip_ben = b.cod_tip_ben and 
      a.nif_abo_liq = b.nif_abo_liq)
LEFT OUTER JOIN dpt_mant_can_reg_1 c ON 
     (b.cod_emb = c.cod_embassament and b.cod_tip_ben = c.cod_tip_ben) 

这有一个错误的表格作为“占优势”的表格。表格 - 因此, 获得不同的结果;你正在写一个不同的查询。

至少,你需要FROM子句看起来更像:

 FROM dpt_mant_canreg_2 b
 LEFT OUTER JOIN dpt_mant_can_reg_3 a
      ON  a.cod_emb = b.cod_emb
      AND a.cod_tip_ben = b.cod_tip_ben
      AND a.nif_abo_liq = b.nif_abo_liq
 LEFT OUTER JOIN dpt_mant_can_reg_1 c
      ON  b.cod_emb = c.cod_embassament
      AND b.cod_tip_ben = c.cod_tip_ben

这具有正确的连接结构(与Informix样式的OUTER连接表示法等效的连接结构),这是使其余部分工作的先决条件。

WHERE子句的其余部分是过滤条件,它们很难理解。你有:

      and b.nif_abo_liq = '39203275M' and b.cod_emb = 1
and (
     (b.dat_cr_ini >= date('01/01/2018') and b.dat_cr_ini <= 
     date('31/12/2018')) 
     or (b.dat_cr_fi >= date('01/01/2018') and b.dat_cr_fi <= 
     date('31/12/2018') 
     or (b.dat_cr_ini <= date('01/01/2018') and b.dat_cr_fi >= 
     date('31/12/2018')) 
     and a.dat_cr_ini = date('01/01/2018') and c.dat_cr_ini = 
     date('01/01/2018')))

格式不会使结构清晰。但是,我们可以将其重新格式化,如下所示:

   AND b.nif_abo_liq = '39203275M'
   AND b.cod_emb = 1
   AND (
        (b.dat_cr_ini >= DATE('01/01/2018') AND b.dat_cr_ini <= DATE('31/12/2018')) 
        OR
        (b.dat_cr_fi >= DATE('01/01/2018') AND b.dat_cr_fi <= DATE('31/12/2018') 
         OR (b.dat_cr_ini <= DATE('01/01/2018') AND b.dat_cr_fi >= DATE('31/12/2018')) 
             AND a.dat_cr_ini = DATE('01/01/2018')
             AND c.dat_cr_ini = DATE('01/01/2018')
        )
       )

这仍然相当复杂,尤其是因为它没有使用BETWEEN / AND。我认为这与上述相同:

   AND b.nif_abo_liq = '39203275M'
   AND b.cod_emb = 1
   AND (
         b.dat_cr_ini BETWEEN DATE('01/01/2018') AND DATE('31/12/2018')
         OR
         b.dat_cr_fi  BETWEEN DATE('01/01/2018') AND DATE('31/12/2018') 
         OR
         ( b.dat_cr_ini <= DATE('01/01/2018') AND
           b.dat_cr_fi  >= DATE('31/12/2018') AND
           a.dat_cr_ini  = DATE('01/01/2018') AND
           c.dat_cr_ini  = DATE('01/01/2018')
         )
       )

OR中的第三个术语是在标准SQL外连接和Informix样式外连接之间产生不同结果的风险,因为它过滤了三个表(另外两个仅在主导表上过滤)。具体来说,对于Informix样式的连接,如果表中的行被别名为a,以满足“2018-01-01或之前的开始”,并在2018-12-31&#39之后完成;条件,将选择那些行,无论表中的行是否具有指定日期相等条件的bc。如果b中没有匹配的行,则会提供空值;同样适用于c

相比之下,标准SQL外连接将过滤器应用于外连接的结果,而bc的贡献为空的行将被删除。

标准的SQL过滤至少有可能是您想要的;如果您使用标准SQL外连接表示法,那么它就是您在Informix中获得的。

上面显示的条件仍然使用Informix风格的DATE函数。诸如DATE('31/12/2018')(假设美式日期约定)之类的日期可以转换为标准SQL DATE符号DATE&#39; 2018-12-31&#39; (这是与语言环境无关的。)

悄悄地忽略了关于过滤方式有多么微妙的观点,可以将查询汇总为:

SELECT a.base_imp,
       b.num_exp,
       c.tip_canon_unit,
       b.cod_tip_ben,
       b.dat_cr_ini,
       b.dat_cr_fi,
       b.cod_emb,
       b.nif_abo_liq,
       0 as usuari,
       '' as nom_usu,
       '' as acotacio
  FROM dpt_mant_canreg_2 b
  LEFT OUTER JOIN dpt_mant_can_reg_3 a
       ON  a.cod_emb = b.cod_emb
       AND a.cod_tip_ben = b.cod_tip_ben
       AND a.nif_abo_liq = b.nif_abo_liq
  LEFT OUTER JOIN dpt_mant_can_reg_1 c
       ON  b.cod_emb = c.cod_embassament
       AND b.cod_tip_ben = c.cod_tip_ben
 WHERE b.nif_abo_liq = '39203275M'
   AND b.cod_emb = 1
   AND (
         b.dat_cr_ini BETWEEN DATE '2018-01-01' AND DATE '2018-12-31'
         OR
         b.dat_cr_fi  BETWEEN DATE '2018-01-01' AND DATE '2018-12-31' 
         OR
         ( b.dat_cr_ini <= DATE '2018-01-01' AND
           b.dat_cr_fi  >= DATE '2018-12-31' AND
           a.dat_cr_ini  = DATE '2018-01-01' AND
           c.dat_cr_ini  = DATE '2018-01-01'
         )
       )

我想针对测试数据库架构和问题中给出的测试数据进行检查,以便比较所需的和实际的结果。可悲的是,我无法 - 在问题中没有给出测试模式或任何测试数据,也没有样本预期结果。能够使用Informix样式的OUTER表示法测试WHERE子句的重构版本以检查它是否干净,这将是很好的。