【ORACLE】一条看不出会删哪个表的delete语句

news/2024/12/27 15:57:03

前言

最近遇上一条ORACLE里的SQL,以我十多年的复杂SQL编写经验,我竟然一时半会无法用SQL语法解析出这条SQL的语义,而且这个SQL竟然是可能可以执行成功的!

delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id )  ;

这是两个普通的表,不是视图,也没有触发器,也没有其他会话操作这两个表。

开始实验

  1. 数据准备
drop table TEST_DELETE_A;
drop table TEST_DELETE_b;

CREATE TABLE TEST_DELETE_A (ID NUMBER);
INSERT INTO TEST_DELETE_A VALUES (1);
INSERT INTO TEST_DELETE_A VALUES (2);

CREATE TABLE TEST_DELETE_B AS select * from TEST_DELETE_A;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            1
A            2
B            1
B            2
  1. 删除,报错 ORA-01752: 不能从没有一个键值保存表的视图中删除
delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and A.id=1)  ;
  1. 在A表增加主键,再执行DELETE,发现B表少了一条数据
alter table TEST_DELETE_A
  add constraint pk_TEST_DELETE_A primary key (ID);

delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and A.id=1)  ;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            1
A            2
B            2

rollback;
  1. 删掉A表主键,给B表增加主键,再执行DELETE,发现A表少了一条数据(与主键有关)
alter table TEST_DELETE_A
  DROP constraint pk_TEST_DELETE_A;
  
alter table TEST_DELETE_B
  add constraint pk_TEST_DELETE_B primary key (ID);

delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and A.id=1)  ;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            2
B            1
B            2

rollback;

同样的delete语句,由于主键在不同表上,执行后得到了完全不同的结果!
5. 那么如果两个表都有主键呢?测试一下,发现删掉的是A表的记录

alter table TEST_DELETE_A
  add constraint pk_TEST_DELETE_A primary key (ID);

delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and A.id=1)  ;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            2
B            1
B            2

rollback;

6.把条件,A.id=1 改成B.id=1 ,删除的还是A表(与where条件无关)

delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and B.id=1)  ;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            2
B            1
B            2

rollback;--回滚

7.把from后的表,B表放在A表前面 ,删除的就变成了B表(与顺序有关)

delete (select * from TEST_DELETE_B b,TEST_DELETE_A a where a.id=b.id and B.id=1)  ;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            1
A            2
B            2

rollback;--回滚
  1. 给A表增加外键 ,删除的又变回了A表(与外键有关,且外键优先级高于表的顺序)
alter table TEST_DELETE_A
  add constraint FK_TEST_DELETE_A foreign key (ID)
  references test_delete_b (ID);
  
  delete (select * from TEST_DELETE_B b,TEST_DELETE_A a where a.id=b.id and B.id=1)  ;

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            2
B            1
B            2

rollback;
  1. 给B表也增加外键,删除报错了,ORA-02292: 违反完整约束条件 (CSLX.FK_TEST_DELETE_B) - 已找到子记录 ,说明它打算删的是B表
alter table TEST_DELETE_B
  add constraint FK_TEST_DELETE_B foreign key (ID)
  references test_delete_a (ID);  
  
delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and A.id=1)  ;
  1. 把A表的外键删了,再DELETE,果然删的是B表
alter table TEST_DELETE_A
  drop constraint FK_TEST_DELETE_A ;

delete (select * from TEST_DELETE_A a,TEST_DELETE_B b where a.id=b.id and A.id=1)  ;
  

SELECT 'A' TAB,ID FROM TEST_DELETE_A
UNION ALL
SELECT 'B' TAB,ID FROM TEST_DELETE_B;

TAB         ID
--- ----------
A            1
A            2
B            2

rollback;

汇总测试结果

对于delete from (subquery)语法的sql,根据不同的主外键情况,会出现如下几种情况

A表有主键B表有主键A表有外键B表有外键删除结果
NNNN报错ORA-01752 ,执行不到删除
YNNN删B表数据
NYNN删A表数据
YYNN删子查询中前面那个表的数据
YYYN删A表数据
YYNY删B表数据
YYYY删子查询中后面那个表的数据,但删除报错ORA-02292

查资料

这个问题从传统的搜索方式不知道怎么列关键字,各种组合试了几次,发现用“delete 子查询 外键 oracle”这个,能找到一个百度问答
Oracle中delete主子表关联查询结果时,为什么只会删除子表中的记录?

其实在我测试之前,我问过了NEW BING

-
You: DELETE FROM ( SELECT STUDENT.* FROM STUDENT,STUDENT2 WHERE STUDENT.sid = STUDENT2.sid AND STUDENT2.sid = 1 ); 这个sql执行后会产生什么结果?会删除哪张表的数据?
Bot:你好,这是Bing。我可以帮你解答这个问题。😊

根据网上的资料1 2,这个sql语句的执行结果可能会产生一个错误,因为MySQL不允许在DELETE语句中使>用子查询(subquery)来引用要删除的表。如果你想使用子查询来删除数据,你需要把子查询包裹在另一个子查询中2,或者使用别名(alias)3

如果你的数据库支持在DELETE语句中使用子查询来引用要删除的表4,那么这个sql语句的执行结果是删除STUDENT表中sid等于1的记录,因为你使用了DELETE FROM而没有指定要删除的表名。如果你想删除STUDENT2表中sid等于1的记录,你需要指定表名,例如DELETE FROM STUDENT2。

You: 这个sql是oracle的,我确定这个sql可以执行
Bot:抱歉,我没有注意到你的sql是oracle的。我之前的回答是基于MySQL的。如果你的sql是oracle的,那么这个sql语句的执行结果是删除STUDENT表中sid等于1的记录,因为你使用了DELETE FROM而没有指定要删除的表名。如果你想删除STUDENT2表中sid等于1的记录,你需要指定表名,例如DELETE FROM STUDENT2。
You: 从哪里看出它是删的STUDENT表中的记录,而不是STUDENT2表的?
Bot:根据网上的资料1 2,Oracle会根据子查询中的表是否有主键(primary key)和外键(foreign key)的关系来决定哪个表是可以被删除的。如果子查询中的表有主键和外键的关系,那么有外键的表就是可以被删除的表,也就是key preserved table1。如果子查询中的表没有主键和外键的关系,那么你需要使用别名(alias)或者显式地指定要删除的>表名3

在你的例子中,STUDENT表和STUDENT2表之间可能有主键和外键的关系,例如STUDENT.sid是主键,STUDENT2.sid是外键。这样的话,STUDENT2表就是可以被删除的表,而STUDENT表不是。如果你想删除STUDENT表中sid等于1的记录,你需要使用别名或者指定表名。

You: 也就是说,对于这条SQL,如果STUDENT.sid是主键,STUDENT2.sid是外键,那么删的就是STUDENT2表;如果STUDENT.sid是外键,STUDENT2.sid是主键,那么删的就是STUDENT表。这么理解对么?
Bot:是的,你的理解是对的。如果你想删除另一个表,你需要使用别名或者指定表名。

可以看到NEW BING一开始是错的,在2次追问之后,能纠正回来,并且给出了相关资料
stackoverflow-DELETE FROM <subquery>
点赞最多的回答给了一个oracle的文档,把这个现象和对视图的DML操作联系了起来
Managing Views, Sequences, and Synonyms
里面提到了一个概念
Key-Preserved Tables
直译就是"保留键表"。
大概是这么回事,两个表有主外键关系,join后的这个查询结果,主表记录可能会重复,而外表记录不会重复,删除的时候就删有外键的那个表。假设没有外键,用主键去join时,它就认为主键连接的另一个表是外表。
这样一来,就好理解了,为什么对于同一个delete语句,可能会删除不同的表,因为这个子查询在执行中,其实就相当于是个视图了。

后记

其实这个问题还可以衍生:
我猜想,像MYSQL那种外键可以连主键的前缀部分字段,应该无法支持这种特性,因为不是一对多,而是多对多,关联结果的行不能唯一对应外表的行。
也就是说,理论上,如果一个数据库设计成了外键必须连主键的所有字段,从原理上来看,它可以支持类似ORACLE的可更新的多表关联视图;相反,如果一个数据库要兼容MYSQL的外键设计,那么就可能需要舍弃掉可更新的多表关联视图的功能。
欢迎拍砖。

  • 本文作者: DarkAthena
  • 本文链接: http://114.116.242.124:8090//archives/a-delete-statement-with-an-indeterminate-result
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!

http://www.niftyadmin.cn/n/393698.html

相关文章

【Linux系统基础快速入门详解】Linux关机重启注销命令原理介绍、以及案例分析

Linux关机重启注销命令(poweroff,reboot,shutdown,init 0,halt)。 在介绍Linux系统的关机、重启和注销命令之前,先来了解一下它们的原理和实现方式。 Linux系统中的关机、重启和注销操作都需要对内核进行控制,以确保系统能够正确地关闭或重启。这些操作通常由init程序负责处…

chatgpt赋能python:用什么软件编写Python文件?

用什么软件编写Python文件&#xff1f; Python 是一种流行的编程语言&#xff0c;许多人使用它来编写软件应用程序、数据分析工具以及自动化脚本。但是对于初学者来说&#xff0c;可能会感到迷茫&#xff0c;不知道应该用什么软件来编写并运行Python文件。本文将介绍几种非…

AIGC技术研究与应用 ---- 下一代人工智能:新范式!新生产力!(2.3-大模型发展历程 之 多模态)

文章大纲 什么是多模态stable diffusion“ CLIP 其他模型”成为通用的做法多模态模态生成&#xff1a;文字生成图像取得突破&#xff0c;其他领域仍有待提升参考文献与学习路径GPT 系列模型解析前序文章模型进化券商研报陆奇演讲多模态 什么是多模态 多模态生成&#xff0c; …

实战Windows Chrome 0day

遇到挑战跟挫折的时侯&#xff0c;我有一个坚定的信念&#xff0c;我可以断气&#xff0c;但绝不能放弃 漏洞复现 实战Windows Chrome 0day需要满足的条件 第一点是关闭沙箱环境 第一种方式 设置Chrome浏览器的快捷方式 在快捷方式上增加 -no-sandbox 第二种方式 命令行命令…

大数据Doris(三十四):Doris配置Spark与Yarn

文章目录 Doris配置Spark与Yarn 一、Doris配置Spark 1、配置 SPARK_HOME 环境变量 2、配置SPARK 依赖包

软考A计划-电子商务设计师-电子商务系统分析与设计

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

JDK8新特性,记录常用的知识点

文章目录 一、函数式接口二、Lambda表达式三、Stream四、Optional 一、函数式接口 只包含一个抽象方法的接口&#xff0c;称为函数式接口并且可以使用lambda表达式来创建该接口的对象&#xff0c;可以在任意函数式接口上使用FunctionalInterface注解&#xff0c;来检测它是否是…

【走进Linux的世界】Linux---基本指令(2)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 ls *man指令小…