使用UNION更新查询

时间:2014-03-17 21:20:31

标签: oracle sql-update union

此查询适用于Oracle9i企业版9.2.0.4.0版:

UPDATE TABLE1 t1 SET t1.FIELD4=(
    SELECT SUM(t4.FIELD4)
    FROM
    (
        SELECT t2.FIELD1, t2.FIELD2, t2.FIELD4
        FROM TABLE1 t2
        UNION
        SELECT t3.FIELD1, t3.FIELD2, t3.FIELD4
        FROM TABLE2 t3
    ) t4
    WHERE t4.FIELD1=t1.FIELD1 AND t4.FIELD2=t1.FIELD2
)

但我想在SELECT子查询中移动WHERE子句,如下所示:

UPDATE TABLE1 t1 SET t1.FIELD4=(
    SELECT SUM(FIELD4)
    FROM
    (
        SELECT t2.FIELD1, t2.FIELD2, t2.FIELD4
        FROM TABLE1 t2
        WHERE t2.FIELD1=t1.FIELD1 AND t2.FIELD2=t1.FIELD2
        UNION
        SELECT t3.FIELD1, t3.FIELD2, t3.FIELD4
        FROM TABLE2 t3
        WHERE t3.FIELD1=t1.FIELD1 AND t3.FIELD2=t1.FIELD2
    )
)

但我有这个错误:

  

ORA-00904:" T1"。" FIELD2" :无效标识符

为什么我不能在子查询中使用t1字段?

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

Oracle返回错误的原因是您无法从相关子查询内部的多个级别引用外部查询中的列。

(StackOverflow上的其他问题也回答了同样的问题。)

<强>更新

我之前(下面)说明的方法只有在我们可以得到一个内联视图时才能工作,其中t1是密钥保留的。 (完全是我的坏。这种方法在MySQL中有效。我已经在MySQL工作了足够长的时间,以至于我期望Oracle会接受相同的语法。)

未指定(fi,fo)中的t1元组是唯一键。因此,相关的子查询是可行的方法。

如果不需要保留NULL值,则可以用零...

替换NULL值
UPDATE TABLE1 t1 
   SET t1.fum = NVL( SELECT SUM(t2.fum)
                       FROM TABLE1 t2
                      WHERE t2.fi = t1.fi
                        AND t2.fo = t1.fo
                   ,0)
              + NVL( SELECT SUM(t3.fum)
                       FROM TABLE2 t3
                      WHERE t3.fi = t1.fi 
                        AND t3.fo = t1.fo
                   ,0)

注意:这将返回零代替NULL;它不保留NULL值。 (如果t1.fum被定义为NOT NULL,那么这不会是一个问题。但是在更一般的情况下,这是一个问题,其中t1.fum在所有行中包含NULL,而TABLE2为空...这将零替换为NULL。

此外,如果t1.fum为NOT NULL,并且元组(fi,fo)是TABLE1中的唯一键,那么我们可以避免执行SELECT来获取存储在t1中的值的SUM。 fum,我们已经有了,所以我们可以......

 ... SET t1.fum = t1.fum + NVL(( correlated subquery ),0)

但是在更一般的情况下,我们想要在t1.fum中保留NULL,我还没有一个很好的方法来引用相关子查询中的t1而不重复子查询(ugghh)......我不知道知道像SUM聚合那样保留NULL的内置运算符或函数,这样我们就可以在添加两个SUM子查询的数值结果时预先设置NULL。


原始答案:

(此要求的用例似乎有点奇怪;请注意UNION将消除重复的行,而UNION ALL则不会。)

原始答案指定了在Oracle中不起作用的多表语法。此方法适用于SELECT语句,但不适用于UPDATE语句。

<击> 就个人而言,我不会使用相关子查询来执行这样的操作;我更喜欢使用JOIN操作来获得相同的结果。例如:

UPDATE TABLE1 t1
  JOIN ( SELECT SUM(DISTINCT r.fum) AS sum_fum, r.fi, r.fo
           FROM ( SELECT t2.fum
                       , t2.fi
                       , t2.fo
                    FROM TABLE1 t2
                   UNION
                  SELECT t3.fum
                       , t3.fi
                       , t3.fo
                    FROM TABLE2 t3
                ) r
          GROUP BY r.fi, r.fo
       ) s
    ON s.fi = t1.fi
   AND s.fo = t1.fo
   SET t1.fum = s.sum_fum

(如果目的是仅更新t1中的行的子集,而不是t1中的所有行,我将省略LEFT关键字以使其成为内部联接。)

但是之前我运行了这样的更新,我首先使用SELECT语句进行验证,例如:

SELECT t1.fi
     , t1.fo
     , t1.fum    AS old_fum
     , s.sum_fum AS new_fum
  FROM TABLE1 t1
  LEFT
  JOIN ( SELECT SUM(DISTINCT r.fum) AS sum_fum, r.fi, r.fo
           FROM ( SELECT t2.fum
                       , t2.fi
                       , t2.fo
                    FROM TABLE1 t2
                   UNION
                  SELECT t3.fum
                       , t3.fi
                       , t3.fo
                    FROM TABLE2 t3
                ) r
          GROUP BY r.fi, r.fo
       ) s
    ON s.fi = t1.fi
   AND s.fo = t1.fo

确认SELECT按照我希望的方式运行后,new_fum列中返回的值是我想要分配给{{1}中fum列的值(替换现有的t1值),只有这样才能将其转换为UPDATE语句。

要将其变为UPDATE语句,我只需替换“SELECT&lt; list&gt;”使用“UPDATE”关键字,只需在语句末尾添加“SET t1.fum = s.sum_fum”赋值(如果查询具有WHERE子句,则在WHERE子句之前)。

注意:我做了以下替换以提高可读性:

old_fum

原始查询中的内联视图使用FIELD1 => fi FIELD2 => fo FIELD4 => fum 运算符而不是UNION,这有点奇怪。由于UNION ALL运算符会在将值添加到一起之前删除UNION的任何重复值...以获得等效结果,因此我在FIELD4内包含DISTINCT关键字聚合

(显然,如果原始查询有SUM运算符,那么我们会在UNION ALL聚合中省略DISTINCT关键字。)

我没有看到原始查询在Oracle 9中如何“有效”;它引用SUM,但内联视图t4.FIELD1不会返回名为t4的列。

<击>