使用 UNION 更新查询

Posted

技术标签:

【中文标题】使用 UNION 更新查询【英文标题】:UPDATE query with UNION 【发布时间】:2014-03-17 21:20:31 【问题描述】:

此查询适用于 Oracle9i Enterprise Edition Release 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 字段?

感谢您的帮助!

【问题讨论】:

我根本看不出该查询是如何工作的。看起来它会在任何版本的 Oracle 中引发异常,因为别名为 t4 的内联视图不会返回名为 FIELD1FIELD2 的列。 见***.com/questions/1233910/… @spencer7593:你是对的!!!我编辑我的帖子 【参考方案1】:

Oracle 返回错误的原因是您不能从外部查询中引用列超过相关子查询内部的一层。

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

更新

我之前说明的方法(如下)只有在我们能够获得一个内联视图(其中 t1 是保留键)时才有效。 (完全是我的错。这种方法在 mysql 中确实有效。我使用 MySQL 的时间已经够长了,我希望 Oracle 会接受相同的语法。)

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

如果不需要保留 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 不是 NULL,并且元组 (fi,fo) 是 TABLE1 中的唯一键,那么我们可以避免执行 SELECT 来获取存储在 t1.fum 中的值的 SUM,我们已经有了,所以我们可以...

 ... 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 列中返回的值就是我想要分配给 t1 中的 fum 列的值(替换现有的old_fum 值),然后我才会将其转换为 UPDATE 语句。

要使其成为 UPDATE 语句,我只需将“SELECT ”替换为“UPDATE”关键字,并在语句末尾添加一个“SET t1.fum = s.sum_fum”赋值(如果查询有 WHERE 子句,则在 WHERE 子句之前)。

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

FIELD1 => fi
FIELD2 => fo
FIELD4 => fum

原始查询中的内联视图使用UNION 运算符而不是UNION ALL 有点奇怪。因为UNION 运算符会在将值相加之前删除FIELD4 的所有重复值...为了获得等效结果,我将DISTINCT 关键字包含在SUM 聚合中。

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

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

【讨论】:

是的,我必须使用 UNION ALL,感谢您的回答,它对我有很大帮助,但更新查询返回 ORA-00971:缺少 SET 关键字 @user1334149:完全是我的错。我一直在使用 MySQL 这么久,我忘记了 Oracle 语法的限制。我说明的那种 JOIN 方法只有在我们将 SELECT 语句转换为内联视图时才有效,并且 t1 是“保留键”。我将使用相关子查询更新我的答案。再说一次,我的错。

以上是关于使用 UNION 更新查询的主要内容,如果未能解决你的问题,请参考以下文章

UNION ALL vs UNION 用于更新/返回+选择?

SQL 查询 UNION 性能

条件查询之or和union

Mysql联合查询union和union all的使用介绍

为啥使用 UNION 运算符的相同查询比使用 UNION ALL 的成本低得多?

Mysql联合查询UNION和UNION ALL的使用介绍