ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Oracle - 两表关联更新

Oracle - 两表关联更新

方法一:MERGE INTO

MERGE INTO语法,主要用于在数据库中根据匹配条件更新数据
适用于需要同时查询A表和B表才能确定更新数据的情况

MERGEINTOTAB1 AUSINGTAB2 BON(A.G_SGMT_ID=B.G_SGMT_IDANDA.GEAR_THR_REF_NO=B.REF_NOANDA.G_TRSC_AMT=B.G_CABL_FEEANDA.CAN_DV_CD<>'2'ANDA.TRSC_DT>='20241118')WHENMATCHEDTHENUPDATESETA.TRSC_MEMO_CTT='测试';

这条语句是一个SQL的MERGE语句,用于将TAB2表中的数据合并到TAB1表中。MERGE语句是一种非常强大的工具,它允许你根据一定的匹配条件来更新目标表中的数据,或者在匹配失败时插入新的数据(虽然在这个特定的例子中并没有包含插入操作)。下面是对这条语句的详细分析:

  1. 目标表(TAB1)和源表(TAB2):

    • MERGE INTO TAB1 A:指定TAB1为目标表,并给它一个别名A。
    • USING TAB2 B:指定TAB2为源表,并给它一个别名B。
  2. 匹配条件:

    • ON (A.G_SGMT_ID = B.G_SGMT_ID AND A.GEAR_THR_REF_NO = B.REF_NO AND A.G_TRSC_AMT = B.G_CABL_FEE AND A.CAN_DV_CD <> '2' AND A.TRSC_DT >= '20241118'):
      • 这些条件用于确定TAB1和TAB2中的哪些行应该被视为匹配。
      • A.G_SGMT_ID = B.G_SGMT_ID:要求两个表中的G_SGMT_ID字段值相同。
      • A.GEAR_THR_REF_NO = B.REF_NO:要求TAB1的GEAR_THR_REF_NO字段值与TAB2的REF_NO字段值相同。
      • A.G_TRSC_AMT = B.G_CABL_FEE:要求两个表中的字段值相同。
      • A.TRSC_DT >= '20241118':要求TAB1中的TRSC_DT(交易日期)字段值大于或等于'20241118'。
  3. 操作:

    • WHEN MATCHED THEN UPDATE SET A.TRSC_MEMO_CTT = '测试';:
      • 当找到匹配的行时,执行更新操作。
      • 将TAB1表中匹配行的TRSC_MEMO_CTT字段更新为'测试'。

注意:

  • 这个MERGE语句只包含了更新操作。如果需要在没有匹配项时插入新数据,可以添加WHEN NOT MATCHED THEN INSERT子句。

方法二:EXISTS

UPDATE需要更新的表名 BSETB.更新字段1='更新内容1',B.更新字段2='更新内容2'WHEREEXISTS(SELECT1FROM关联表名 AWHEREA.关联字段=B.关联字段ANDA.筛选字段1='筛选字段1'ANDA.筛选字段2='筛选字段2'ANDB.筛选字段3='筛选字段3');

问题分析

需要写一个更新语句,但是更新的判断条件是两个表关联查询出来的

踩坑一

一开始是打算UPDATE写两个表,然后WHERE里面两表关联,就像这样:

--错误sqlUPDATE表名1A,表名2BSETA.更新字段1='更新内容1',A.更新字段2='更新内容2'WHEREA.关联字段=B.关联字段ANDA.筛选字段1='筛选字段1'ANDA.筛选字段2='筛选字段2'ANDB.筛选字段3='筛选字段3'

但是一直报错
很明显也不是缺失SET 关键字的问题

踩坑二

之后搜索怎么在Oracel关联两表更新。发现了下面这种写法

--正常sqlUPDATE更新表名 ASETA.更新字段1='更新内容1'WHEREEXISTS(SELECT1FROM关联表名 BWHEREA.关联字段=B.关联字段)

但是也是没太仔细看,加上思维固化,写成了下面这种情况

--错误sqlUPDATE更新表名 ASETA.更新字段1='更新内容1'WHEREEXISTS(SELECT1FROM更新表名 A,关联表名 BWHEREA.关联字段=B.关联字段ANDA.筛选字段1='筛选字段1')

可以看到在FROM里面比搜索出来的sql语句,多了更新的表名A

显而易见,还会有点问题
但是这次并不是报错,而是筛选不是自己预想的筛选,导致更新的数据非常多

像下图一样,更新语句跑了1分半,更新了46万条数据。。。
明显是有问题的(实际上只需要更新几十条),被更新的B表实际上也只有26万条数据,不知道46万条更新怎么来的
只能先回滚更新

通过对比发现是FROM多写了个更新的表名,修改之后可以正常的更新,没有问题

踩坑三

既然已经可以两表关联查询,更新其中一个表了,那能不能同时更新两个表呢?

想当然的认为sql应该是这样的

UPDATE更新表1ASETA.更新字段1='A表更新内容1',B.更新字段2='B表更新内容2'WHEREEXISTS(SELECT1FROM更新表2BWHEREA.关联字段=B.关联字段ANDA.筛选字段='筛选字段')

于是写出了下面的sql,但是事实证明这样写是不行的

语句分析

UPDATE需要更新的表名 BSETB.更新字段1='更新内容1',B.更新字段2='更新内容2'WHEREEXISTS(SELECT1FROM关联表名 AWHEREA.关联字段=B.关联字段ANDA.筛选字段1='筛选字段1'ANDA.筛选字段2='筛选字段2'ANDB.筛选字段3='筛选字段3');

A表和B表都有相同的关联字段,并且通过筛选字段校验。那么就有SELECT 1,那么EXISTS就会返回TRUE;最后进行更新操作。

EXISTS

EXISTS用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False

EXISTS(包括 NOT EXISTS )子句的返回值是一个BOOL值。 EXISTS内部有一个子查询语句(SELECT … FROM…), 我将其称为EXISTS的内查询语句。其内查询语句返回一个结果集。 EXISTS子句根据其内查询语句的结果集空或者非空,返回一个布尔值。

此时在该更新sql中

--EXISTS的内查询语句为:SELECT1FROM关联表名 AWHEREA.关联字段=B.关联字段ANDA.筛选字段1='筛选字段1'ANDA.筛选字段2='筛选字段2'ANDB.筛选字段3='筛选字段3'--WHERE判断EXISTS返回的BOOL值来决定是否更新WHEREEXISTS

SELECT 1

当我们只关心数据表有多少记录行而不需要知道具体的字段值时,SELECT 1 FROM TABLE是一个很不错的SQL语句写法,它通常用于子查询。

SELECT 1 FROM TABLE可以减少系统开销,提高运行效率。因为此时数据库就不会去检索数据表里每条具体的记录和每条记录里每个具体的字段值并将它们放到内存里,而是查询到有多少记录行存在就输出多少个“1”,每个“1”代表有1行记录。

选用数字1是因为它所占用的内存空间最小,用数字0的效果也一样,即:SELECT 0 FROM TABLE。

从效率上来说,SELECT 1 > SELECT 字段 > SELECT *

在子查询中的应用
常规写法

SELECT*FROMt1 aWHEREEXISTS(SELECT*FROMt2 bWHEREa.id=b.id)

更优写法

SELECT*FROMt1 aWHEREEXISTS(SELECT1FROMt2 bWHEREa.id=b.id)
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进