SQL Server中UPDATE和DELETE语句结合INNER/LEFT/RIGHT/FULL JOIN的用法
在SQL Server中,UPDATE和DELETE语句是可以结合INNER/LEFT/RIGHT/FULL JOIN来使用的。
我们首先在数据库中新建两张表:
[T_A]
CREATE TABLE [dbo].[T_A](
[ID] [int] NOT NULL,
[Name] [nvarchar](50) NULL,
[Age] [int] NULL,
CONSTRAINT [PK_T_A] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
[T_B]
CREATE TABLE [dbo].[T_B](
[ID] [int] NOT NULL,
[Name] [nvarchar](50) NULL,
[Age] [int] NULL,
CONSTRAINT [PK_T_B] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
UPDATE与INNER/LEFT/RIGHT/FULL JOIN
UPDATE结合INNER JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); UPDATE [T_A]
SET
Age=[T_B].Age
FROM
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面INNER JOIN查询,先找出表[T_A]的数据记录,然后UPDATE这些找出的数据记录:
SELECT
[T_A].*,
[T_B].*
FROM
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
注意如果表[T_A]中的某行数据与表[T_B]中多行数据匹配上,这种情况下,表[T_A]的该行数据也只会被UPDATE一次,不过用表[T_B]中的哪一行匹配数据去UPDATE表[T_A]是不确定的。
UPDATE结合LEFT JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); UPDATE [T_A]
SET
Age=[T_B].Age
FROM
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面LEFT JOIN查询,先找出表[T_A]的数据记录,然后UPDATE这些找出的数据记录:
SELECT
[T_A].*,
[T_B].*
FROM
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
UPDATE结合RIGHT JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300),
(4,N'Mike',400),
(5,N'Bob',500),
(6,N'Clark',600),
(7,N'Sam',700); UPDATE [T_A]
SET
Age=[T_B].Age
FROM
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面RIGHT JOIN查询,先找出表[T_A]的数据记录,然后UPDATE这些找出的数据记录:
SELECT
[T_A].*,
[T_B].*
FROM
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
UPDATE结合FULL JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); UPDATE [T_A]
SET
Age=[T_B].Age
FROM
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面FULL JOIN查询,先找出表[T_A]的数据记录,然后UPDATE这些找出的数据记录:
SELECT
[T_A].*,
[T_B].*
FROM
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
DELETE与INNER/LEFT/RIGHT/FULL JOIN
DELETE结合INNER JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); DELETE [T_A]
FROM
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面INNER JOIN查询,先找出表[T_A]的数据记录,然后DELETE这些找出的数据记录:
SELECT
[T_A].*
FROM
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
DELETE结合LEFT JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); DELETE [T_A]
FROM
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面LEFT JOIN查询,先找出表[T_A]的数据记录,然后DELETE这些找出的数据记录:
SELECT
[T_A].*
FROM
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
DELETE结合RIGHT JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300),
(4,N'Mike',400),
(5,N'Bob',500),
(6,N'Clark',600),
(7,N'Sam',700); DELETE [T_A]
FROM
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面RIGHT JOIN查询,先找出表[T_A]的数据记录,然后DELETE这些找出的数据记录:
SELECT
[T_A].*
FROM
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
DELETE结合FULL JOIN:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); DELETE [T_A]
FROM
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

其效果相当于通过下面FULL JOIN查询,先找出表[T_A]的数据记录,然后DELETE这些找出的数据记录:
SELECT
[T_A].*
FROM
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID;
JOIN语句使用子查询
其实我们还可以在UPDATE和DELETE语句使用JOIN时,对UPDATE和DELETE的表使用子查询,但是这种用法我个人不推荐,我们来看一个UPDATE的例子:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); UPDATE [T_A]
SET
Age=[T_B].Age
FROM
(
SELECT
*
FROM [T_A]
WHERE
[T_A].ID<=2
) AS [T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
表[T_A]的结果如下所示:

可以看到由于我们现在对表[T_A]做了子查询,用WHERE条件限制了其ID<=2,所以子查询只会返回表[T_A]的两条数据,因此最终表[T_A]只有两条数据得到了更新。
这个结果是符合我们预期的,但是其中有一个很重要的因素,就是UPDATE关键字后面的表名要和子查询的别名一致,我们对上面的UPDATE语句稍作修改,如下所示:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); UPDATE [T_A]
SET
Age=[T_B].Age
FROM
(
SELECT
*
FROM [T_A]
WHERE
[T_A].ID<=2
) AS [T_A_1]
INNER JOIN
[T_B]
ON [T_A_1].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
现在我们将子查询的名字命名为了[T_A_1],但是我们UPDATE的表是[T_A],上面语句执行后,表[T_A]的结果如下所示:

我们可以看到表[T_A]的所有数据都被莫名其妙地更新了,我们来看看UPDATE语句的执行计划,如下所示:

我们可以看到最关键的一个步骤,也就是表[T_A]和JOIN结果集之间的"Nested Loops"这个JOIN有个警告:"No Join Predicate",其含义就是说表[T_A]和JOIN结果集之间的JOIN是没有ON条件的,相当于CROSS JOIN,所以我们最后才看到表[T_A]的所有数据都被莫名其妙地更新了。这是因为现在UPDATE语句后面的表名[T_A]和子查询的命名[T_A_1]不一致,所以UPDATE语句现在不知道如何将[T_A_1]和[T_B]之间INNER JOIN后的结果集对应到UPDATE的表[T_A]中,所以就将表[T_A]的所有数据都更新了。
要解决这个问题其实也很简单,只要将UPDATE语句后面的表名改为子查询的名字[T_A_1],使得UPDATE语句后面的表名和子查询的名字一致就行了,如下所示:
TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B]; INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50); INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300); UPDATE [T_A_1]
SET
Age=[T_B].Age
FROM
(
SELECT
*
FROM [T_A]
WHERE
[T_A].ID<=2
) AS [T_A_1]
INNER JOIN
[T_B]
ON [T_A_1].ID=[T_B].ID; SELECT * FROM [dbo].[T_A];
现在,表[T_A]的结果如下所示:

可以看到这次UPDATE语句正确地只更新了表[T_A]的两行数据,我们看看UPDATE语句的执行计划:

可以看到这次"Nested Loops"没有任何警告,正确地将表[T_A]和[T_B]进行了INNER JOIN,所以UPDATE语句只更新了表[T_A]的两行数据。这说明虽然我们在UPDATE语句后面写的是子查询的名字[T_A_1],但是UPDATE语句还是可以根据子查询[T_A_1]知道要更新的表实际上是[T_A],不得不说这一点SQL Server还是挺智能的。
但是鉴于在UPDATE和DELETE语句中使用JOIN时,再对UPDATE和DELETE的表使用子查询看起来比较怪,并且如上所示,用得不对会造成结果出错,所以我个人还是不推荐在UPDATE和DELETE语句中使用JOIN时,再对UPDATE和DELETE的表使用子查询,况且这种子查询实际上完全可以用其它方式来替代。
总结
举了这么多例子,其实我个人觉得UPDATE和DELETE语句与INNER JOIN结合使用才是最有用的,但是不管是什么JOIN,从上面的例子可以看出,其实都相当于是先用SELECT语句做表[T_A]的INNER/LEFT/RIGHT/FULL JOIN查询,然后UPDATE或DELETE表[T_A]中查询出的这些数据记录。
SQL Server中UPDATE和DELETE语句结合INNER/LEFT/RIGHT/FULL JOIN的用法的更多相关文章
- SQL Server 基本UPDATE和DELETE语句
1.UPDATA 基本UPDATE语法:(可以修改多行的列) 2.DELETE
- 【SQL Server学习笔记】Delete 语句、Output 子句、Merge语句
原文:[SQL Server学习笔记]Delete 语句.Output 子句.Merge语句 DELETE语句 --建表 select * into distribution from sys.obj ...
- SQL Server 中使用参数化Top语句
在T-Sql中,一般top数据不确定的情况下,都是拼sql,这样无论是效率还是可读性都不好.应该使用下面参数化Top方式:declare @TopCount int set @TopCount = 1 ...
- sql server中的while循环语句
语法格式: while 条件 begin ....... end declare @num begin update SDetail end
- SQL SERVER中查询某个表或某个索引是否存在
查询某个表是否存在: 在实际应用中可能需要删除某个表,在删除之前最好先判断一下此表是否存在,以防止返回错误信息.在SQL SERVER中可通过以下语句实现: IF OBJECT_ID(N'表名称', ...
- SQL server触发器中 update insert delete 分别给写个例子被。
SQL server触发器中 update insert delete 分别给写个例子以及解释下例子的作用和意思被, 万分感谢!!!! 主要想知道下各个语句的书写规范. INSERT: 表1 (ID, ...
- SQL Server中常用的SQL语句(转):
SQL Server中常用的SQL语句 转自:http://www.cnblogs.com/rainman/archive/2013/05/04/3060428.html 1.概述 名词 笛卡尔积.主 ...
- SQL Server中常用的SQL语句
1.概述 名词 笛卡尔积.主键.外键 数据完整性 实体完整性:主属性不能为空值,例如选课表中学号和课程号不能为空 参照完整性:表中的外键取值为空或参照表中的主键 用户定义完整性:取值范围或非空限制,例 ...
- SQL Server中CURD语句的锁流程分析
我只在数据库选项已开启“行版本控制的已提交读”(READ_COMMITTED_SNAPSHOT为ON)中进行了观察. 因此只适用于这种环境的数据库. 该类数据库支持四种不同事务隔离级别,下面分别观察数 ...
随机推荐
- Android--记录莫名其妙的引用、依赖冲突解决办法
这几天一直在忙于其他项目,打开之前的项目,我曹,代码什么都没动,为何莫名其妙的的就出现冲突了.(我猜测是依赖的其它三方库,库里面的依赖发生改变导致的) 全是这种错误,各种冲突导致的,记录一下,以下指示 ...
- (后端)Spring Boot自定义错误页面,Whitelabel Error Page处理方式(转)
我已经是Spring Framework框架的忠实粉丝.对于企业软件开发者来说它提供了对常见问题的通用解决方案,包括那些你在未来开发中没有意识到的问题.但是,它构建的J2EE项目变得比较臃肿,需要被一 ...
- go 利用orm简单实现接口分布式锁
在开发中有些敏感接口,例如用户余额提现接口,需要考虑在并发情况下接口是否会发生问题.如果用户将自己的多条提现请求同时发送到服务器,代码能否扛得住呢?一旦没做锁,那么就真的会给用户多次提现,给公司带来损 ...
- MySQL 约束、表连接、表关联、索引
一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为空,用来保证数据完整性. 外键:是另一表的唯一性 ...
- 2019年Web前端入门的自学路线
本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文.本文内容不定期更新. 我前几天写过一篇文章:<裸辞两个月,海投一个月 ...
- coTurn测试程序之turnutils_stunclient.exe
测试使用方法,作为STUN客户端,仅用于测试STUN服务是否正常运行. 使用coTurn服务启动STUN/TURN服务后,执行以下命令即可: turnutils_stunclient -p 61.18 ...
- Docker & pure-ftpd 快速加建 FTP 服务器
项目需要进行升级服务,现在需要基于centos 7使用docker来快速打架一个FTP环境来方便本地文件上传. 本次使用的是 pure-ftpd docker镜像,有关镜像使用的详细信息,本人是从 h ...
- MyBatis笔记----mybatis分页
mybatis版本3.4以下 结构 spring-mvc.xml <?xml version="1.0" encoding="UTF-8"?> &l ...
- [20190312]视图v$datafile字段OFFLINE_CHANGE#, ONLINE_CHANGE#.txt
[20190312]视图v$datafile字段OFFLINE_CHANGE#, ONLINE_CHANGE#.txt --//视图v$datafile存在2个字段OFFLINE_CHANGE#, O ...
- MySQL 数据库 简单操作命令 (部分总结)
1.查看进程方式查看数据库 ps - ajx|grep mysql 2.登录 MySQL mysql -u用户名 -p密码 3.开启服务 sudo service mysql start 4.停止服务 ...