Oracle“ORA-38104: 无法更新ON子句中引用的列”解决方式 merge (oracle中merge用法) [Oracle] Merge语句

今天在plsql中使用merge into更新字段时遇到了一个问题。

相关代码如下:

    MERGE INTO TABLE1 A
    USING (SELECT T.ID, T.NEW_CORE_SIZE FROM TABLE2 T) B
    ON (A.ID = B.ID AND A.CORE_SIZE IS NULL)
    WHEN MATCHED THEN
      UPDATE SET A.CORE_SIZE = B.NEW_CORE_SIZE;

这里请注意,在ON中出现的字段,不可以在WHEN MATCHED THEN中更新,所以无法更新A.CORE_SIZE字段

正确的写法如下:

    MERGE INTO TABLE1 A
    USING (SELECT T.ID, T.NEW_CORE_SIZE FROM TABLE2 T) B
    ON (A.ID = B.ID)
    WHEN MATCHED THEN
      UPDATE SET A.CORE_SIZE = DECODE(A.CORE_SIZE,null,B.NEW_CORE_SIZE,A.CORE_SIZE);

merge (oracle中merge用法) - chenhonggao - 博客园 (cnblogs.com)

merge (oracle中merge用法)

 

[Oracle] Merge语句

Merge的语法例如以下:

 

MERGE [hint] INTO [schema .] table [t_alias] USING [schema .] 
{ table | view | subquery } [t_alias] ON ( condition ) 
WHEN MATCHED THEN merge_update_clause 
WHEN NOT MATCHED THEN merge_insert_clause;
MERGE是什么,怎样使用呢?让我们先看一个简单的需求:

 

需求是,从T1表更新数据到T2表中。假设T2表的NAME 在T1表中已存在,就将MONEY累加,假设不存在。将T1表的记录插入到T2表中。

大家知道,在等价的情况下,一定需要至少两条语句,一条为UPDATE,一条为INSERT,并且语句中必需要与推断的逻辑,或者写在过程中,假设是单条语句,就要写全条件。
写在UPDATE和INSERT的语句中,显的比较麻烦并且easy出错。假设了解MERGE,我们能够不借助存储过程,直接用单条SQL便实现了该业务逻辑,且代码非常简洁。详细例如以下:

 

MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=T1.MONEY+T2.MONEY
WHEN NOT MATCHED THEN
INSERT
VALUES (T1.NAME,T1.MONEY);

 

Merge的四大灵活之处

上面讲了Merge的语法和基本使用方法,其实Merge能够很灵活。
1.UPDATE和INSERT动作可仅仅出现其一(9I必须同一时候出现。)
--我们可选择只UPDATE目标表
MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=T1.MONEY+T2.MONEY;

--也可选择只INSERT目标表而不做不论什么UPDATE动作

MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN NOT MATCHED THEN
INSERT
VALUES (T1.NAME,T1.MONEY);
2.可对MERGE语句加条件
MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=T1.MONEY+T2.MONEY
WHERE T1.NAME='A';
3.可用DELETE子句清除行
/*

在这样的情况下,首先是要先满足T1.NAME=T2.NAME的记录,假设T2.NAME=’A’并不满足T1.NAME=T2.NAME过滤出的记录集,
那这个DELETE是不会生效的。在满足的条件下,能够删除目标表的记录。

*/

MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=T1.MONEY+T2.MONEY
DELETE WHERE (T2.NAME = 'A');
4.可采用无条件方式Insert
/*
方法非常easy,在语法ONkeyword处写上恒不等条件(如1=2)后,MATCHED语句的INSERT就变为无条件INSERT了,详细例如以下
*/


MERGE INTO T2
 USING T1
 ON (1=2)
 WHEN NOT MATCHED THEN
 INSERT
VALUES (T1.NAME,T1.MONEY);

Merge的误区

1. 不能更新ON子句引用的列
MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.NAME=T1.NAME;

ORA-38104: 无法更新 ON 子句中引用的列: "T2"."NAME"
2. DELETE子句的WHERE顺序必须最后
MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=T1.MONEY+T2.MONEY
DELETE WHERE (T2.NAME = 'A')
WHERE T1.NAME='A';

ORA-00933: SQL 命令未正确结束
3.DELETE 子句仅仅能够删除目标表。而无法删除源表
/*
 这里须要引起注意,不管DELETE WHERE (T2.NAME = 'A' )这个写法的T2是否改写为T1。效果都一样,都是对目标表进行删除。
*/

SELECT * FROM T1;
NAME                      MONEY
-------------------- ----------
A                            10
B                            20

SELECT * FROM T2;
NAME                      MONEY
-------------------- ----------
A                            30
C                            20

MERGE INTO T2
  USING T1
  ON (T1.NAME=T2.NAME)
  WHEN MATCHED THEN
  UPDATE
  SET T2.MONEY=T1.MONEY+T2.MONEY
  DELETE WHERE (T2.NAME = 'A' );
  
  
SELECT * FROM T1;

NAME                      MONEY
-------------------- ----------
A                            10
B                            20


SELECT * FROM T2;

NAME                      MONEY
-------------------- ----------
C                            20

4.更新同一张表的数据,需操心USING的空值
SELECT * FROM T2;
NAME                      MONEY
-------------------- ----------
A                            30
C                            20

/*

需求为对T2表进行自我更新。假设在T2表中发现NAME=D的记录,就将该记录的MONEY字段更新为100,假设NAME=D的记录不存在,
则自己主动添加。NAME=D而且MONEY=100的记录。依据语法完毕例如以下代码:

*/

MERGE INTO T2
USING (select * from t2 where NAME='D') T
ON (T.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=100
WHEN NOT MATCHED THEN
INSERT
VALUES ('D',200);



--可是查询发现。本来T表应该由于NAME=D不存在而要添加记录。可是实际却根本无变化。
SQL> SELECT * FROM T2;
NAME                      MONEY
-------------------------------------------------------
A                            30
C                            20

/*
   原来是由于此时select * from t2 where NAME='D'为NULL,所以出现了无法插入的情况。
   我们能够利用COUNT(*)的值不会为空的特点来等价改造。详细例如以下:
*/

MERGE INTO T2
USING (select COUNT(*) CNT from t2 where NAME='D') T
ON (T.CNT<>0)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=100
WHEN NOT MATCHED THEN
INSERT
VALUES ('D',100);


SQL> SELECT * FROM T2;
NAME                      MONEY
-------------------------------
A                            30
C                            20
D                           100
5. 必需要在源表中获得一组稳定的行
---构造数据,请注意这里多插入一条A记录,就产生了ORA-30926错误
INSERT INTO T1 VALUES ('A',30);
COMMIT;

---此时继续运行例如以下
MERGE INTO T2
USING T1
ON (T1.NAME=T2.NAME)
WHEN MATCHED THEN
UPDATE
SET T2.MONEY=T1.MONEY+T2.MONEY;
ORA-30926: 无法在源表中获得一组稳定的行

/*
oracle中的merge语句应该保证on中的条件的唯一性,T1.NAME=T2.NAME的时候。T1表记录相应到了T2表的两条记录,所以就出错了。

 

解决方法非常easy。比方我们能够对T1表和T2表的关联字段建主还键,这样基本上就不可能出现这种问题,并且一般而言,MERGE语句的关联字段互相有主键。 MERGE的效率将比较高!或者是将T1表的ID列做一个聚合。这样归并成单条,也能避免此类错误。

如: */ MERGE INTO T2 USING (select NAME,SUM(MONEY) AS MONEY FROM T1 GROUP BY NAME)T1 ON (T1.NAME=T2.NAME) WHEN MATCHED THEN UPDATE SET T2.MONEY=T1.MONEY+T2.MONEY; --正常情况下,一般出现反复的NAME须要引起怀疑,不太应该。

oracle-merge into using on用法_张三李四dw的博客-CSDN博客_merge into using on

在sql中看到如下
截图:
在这里插入图片描述

merge into 表1 using 表2 on(.... and ...) 
when matched then ....
上图大概样式

那么表达什么意思?


以下内容摘自博文:
https://blog.csdn.net/qq_34745941/article/details/81176140?utm_source=copy

1. 适用场景:'有则更新,无则插入'

2. 好处
   (1) 执行 '效率高'
   (2) 语法简洁

语法:

merge into 目标表 b
using 源表 a
on (b.字段1 = a.字段1 and b.字段n = a.字段n) -- 必须带 '()'
when matched then -- 整体扫描,匹配时,执行此处
   update 子句
when not matched then -- 整体扫描,不匹配时,执行此处
   insert 子句;

 

 

Oracle中时间日期转化函数to_date和to_char用法总结_旺旺_123的博客-CSDN博客_to_char函数用法 日期

  在实际的工作中会经常会用到to_char()、to_date()函数来对时间、日期进行处理。

1、to_char()函数的用法

 1.1、将时间日期按照指定的格式输出,得到的是字符串,而非date类型。

 

 
  1. select sysdate,to_char(sysdate,'yyyy-mm-dd')from dual;
  2. select sysdate,to_char(sysdate,'yyyy/mm/dd')from dual;
  3. select sysdate,to_char(sysdate,'yyyymmdd')from dual;
  4. select sysdate,to_char(sysdate,'yyyymmdd hh24:mi:ss')from dual;
 

运行的输出结果为:

 

 

 
  1. 2017/6/15 17:07:24 2017-06-15
  2. 2017/6/15 17:07:25 2017/06/15
  3. 2017/6/15 17:07:25 20170615
  4. 2017/6/15 17:07:25 20170615 17:07:25
 

 1.2、用to_char()可以得到日期中的年、月、日、时、分

 
  1. select sysdate,to_char(sysdate,'yyyy')from dual;
  2. select sysdate,to_char(sysdate,'mm')from dual;
  3. select sysdate,to_char(sysdate,'hh24')from dual;
  4. select sysdate,to_char(sysdate,'mi')from dual;
 

运行的输出结果为:

 

 

 
  1. 2017/6/15 17:09:14 2017
  2. 2017/6/15 17:09:14 06
  3. 2017/6/15 17:09:14 17
  4. 2017/6/15 17:09:14 09
 

 

注:to_char()得到的是字符串,要查询具体单日、时、分要特别注意。 

 

 
  1. select accept_time,to_char(accept_time,'mi') from TMP_WW_0615_GYTS_S2
  2. where to_char(accept_time,'mi')='06' ;
  3. select accept_time,to_char(accept_time,'mi') from TMP_WW_0615_GYTS_S2
  4. where to_char(accept_time,'mi')='6' ;
 

 运行输出结果为:

 

 

 
  1. 2017/6/8 21:06:59 06
  2. null
 

 

2、to_date()函数的用法

 2.1、将字符串转换为具体指定的时间日期格式

 

 
  1. select sysdate,to_date('20170615','yyyymmdd')from dual;
  2. select sysdate,to_date('20170615','yyyy-mm-dd')from dual;
  3. select sysdate,to_date('20170615','yyyy/mm/dd')from dual;
  4. select sysdate,to_date('20170615','yyyy-mm-dd hh24:mi:ss')from dual;
 

 运行输出结果为:

 

 

 
  1. 2017/6/15 17:20:27 2017/6/15
  2. 2017/6/15 17:20:27 2017/6/15
  3. 2017/6/15 17:20:27 2017/6/15
  4. 2017/6/15 17:20:27 2017/6/15
 

 

注:to_date()得到的日期格式是和系统的日期格式保持一致;

      得到的时间为当天的 00 :00:00。

2.2、可以直接使用date'yyyy-mm-dd'

 

 select date'2017-5-1',to_date('20170615','yyyymmdd')from dual; 

 

 运行输出结果为:

 

 2017/5/1	2017/6/15

注:date'2017/5/1' 会提示格式不对。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章