SQL Server中CROSS APPLY连接操作

在 SQL Server 中,CROSS APPLY 是一个连接操作,它类似于 INNER JOIN,但有一些关键差异,特别是在处理表值函数(TVF)、行集函数或子查询时。CROSS APPLY 返回对于外部查询中的每一行,在内部查询或函数中都存在的行。但是,如果内部查询或函数对于外部查询的某行没有返回任何行,则该外部查询的行不会出现在结果集中。

以下是一些关键点,用于理解 CROSS APPLY:

  • 与 INNER JOIN 的比较:虽然 CROSS APPLY 在某些情况下可能产生与 INNER JOIN 相同的结果,但它们在处理一对多关系时有所不同。特别是当内部查询或函数返回多行时,CROSS APPLY 会为外部查询的每一行与内部查询或函数的每一行组合生成结果。
  • 与子查询的比较:与在 SELECT 语句中使用的子查询不同,CROSS APPLY 中的子查询或函数可以引用外部查询的列,就像它们在 JOIN 条件中一样。
  • 表值函数:CROSS APPLY 经常与表值函数一起使用,特别是当这些函数返回基于外部查询行值的动态结果集时。
  • 性能:在某些情况下,CROSS APPLY 可能会比使用其他连接类型(如 INNER JOIN)的查询更快,因为它可以更早地过滤掉不满足条件的行。但是,性能总是取决于具体的查询和数据分布。

假设我们有两个表:T_Product 和 T_Product_imgs。每个产品可能有多个上传的图片,但我们想为每个产品选择其最新图片。这可以通过使用 CROSS APPLY 和一个子查询来实现,该子查询返回每个产品的最新图片。

sql 复制代码
CREATE TABLE T_Product
(
  ID BIGINT IDENTITY(1,1) CONSTRAINT PK_ProductID PRIMARY KEY,
  Title NVARCHAR(64) NOT NULL CONSTRAINT DF_ProductTitle DEFAULT N'',
  Tag NVARCHAR(64) NOT NULL CONSTRAINT DF_ProductTag DEFAULT N'',
  Price DECIMAL(18,6) NOT NULL,
  CreateUser NVARCHAR(32) NOT NULL,
  CreateDate DATETIME DEFAULT GETDATE()
)

CREATE TABLE T_Product_imgs
(
  ID BIGINT IDENTITY(1,1) CONSTRAINT PK_Product_imgsID PRIMARY KEY,
  Pro_id BIGINT NOT NULL,
  imgName NVARCHAR(64),
  CreateUser NVARCHAR(32) NOT NULL,
  CreateDate DATETIME DEFAULT GETDATE()
)
ALTER TABLE T_Product_imgs ADD CONSTRAINT FK_Product_imgs_ID FOREIGN KEY (Pro_id) REFERENCES T_Product (id) 

INSERT INTO T_Product (Title,Tag,Price,CreateUser,CreateDate) VALUES(N'雷霆打码机',N'打码',900000.89,N'DBA卢飞虎','2020-08-08 23:20:56')
INSERT INTO T_Product (Title,Tag,Price,CreateUser,CreateDate) VALUES(N'大族钻机',N'钻孔',1050000.66,N'DBA卢飞虎','2020-09-01 23:20:56')
INSERT INTO T_Product (Title,Tag,Price,CreateUser,CreateDate) VALUES(N'雪龙锣机',N'锣板',530000.76,N'DBA毛一飞',GETDATE()-30)


INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(1,'XL-20200801161136.jpg',N'DBA-JAVA','2020-08-01 23:20:56')
INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(2,'LT-20200906162236.jpg',N'DBA卢飞虎','2020-09-06 13:20:26')
INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(3,'DC-20210808163336.jpg',N'DBA卢飞虎','2021-08-08 23:20:56')
INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(3,'DC-20211005163336.jpg',N'DBA卢飞虎','2021-10-05 22:20:56')
INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(2,'LT-20210609162236.jpg',N'Oracle','2021-06-09 20:20:56')
INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(1,'XL-20210808161136.jpg',N'JAVA','2021-08-08 23:20:56')
INSERT INTO T_Product_imgs (Pro_id,imgName,CreateUser,CreateDate) VALUES(1,'XL-20200608161136.jpg',N'MongDB','2020-06-08 23:20:56')


SELECT * FROM T_Product

SELECT * FROM T_Product_imgs ORDER BY Pro_id,CreateDate DESC 
  • 使用INNER JOIN 和子查询使用ROW_NUMBER() OVER字句
sql 复制代码
SELECT a.id,a.title,a.Tag,a.Price,T.Pro_id,T.imgName
FROM T_Product AS a
  INNER JOIN  ( SELECT Pro_id,imgName,CreateDate,ROW_NUMBER() OVER(PARTITION BY Pro_id  ORDER BY CreateDate DESC) AS Rn 
			    FROM T_Product_imgs AS b ) AS T
ON a.id=T.Pro_id
WHERE T.Rn=1
  • CROSS APPLY 实现
    与子查询的比较:与在 SELECT 语句中使用的子查询不同,CROSS APPLY 中的子查询或函数可以引用外部查询的列
sql 复制代码
SELECT a.id,a.title,a.Tag,a.Price,T.Pro_id,T.imgName
FROM T_Product as a
 CROSS APPLY ( SELECT TOP 1 b.pro_id,b.imgName 
               FROM T_Product_imgs AS b 
			   WHERE b.Pro_id=a.id 
			   ORDER BY b.CreateDate DESC) AS T

在上面的示例中,对于 T_Product 表中的每一行,子查询都会返回最新的产品图片 imgName (如果存在)。如果某个产品没有产品图片,则该产品的信息不会出现在结果集中。

sql 复制代码
INSERT INTO T_Product (Title,Tag,Price,CreateUser,CreateDate) VALUES(N'大板X99',N'主板',230670.00,N'DBA毛一飞',GETDATE()-30)

插入T_Product 表的ID=4数据,但没有在T_Product_imgs表中插入最新的产品图片 imgName

sql 复制代码
SELECT a.id,a.title,a.Tag,a.Price,T.Pro_id,T.imgName
FROM T_Product as a
 CROSS APPLY ( SELECT TOP 1 b.pro_id,b.imgName 
               FROM T_Product_imgs AS b 
			   WHERE b.Pro_id=a.id 
			   ORDER BY b.CreateDate DESC) AS T

再次执行,发现T_Product 表的ID=4数据是没有出现在结果集中。充分验证如果内部查询或函数对于外部查询的某行没有返回任何行,则该外部查询的行不会出现在结果集中。

相关推荐
Ciderw10 分钟前
MySQL日志undo log、redo log和binlog详解
数据库·c++·redis·后端·mysql·面试·golang
CT随38 分钟前
Redis 存在线程安全问题吗?为什么?
数据库·redis·安全
TravisBytes40 分钟前
Redis如何解决热Key问题
数据库·redis·缓存
宽带你的世界44 分钟前
TiDB 是一个分布式 NewSQL 数据库
数据库·分布式·tidb
隔壁老王1561 小时前
tidb实时同步到mysql
数据库·mysql·tidb
2501_903238651 小时前
深入理解 JUnit 的 @RunWith 注解与自定义 Runner
数据库·junit·sqlserver·个人开发
小光学长1 小时前
基于flask+vue框架的的医院预约挂号系统i1616(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库
听封1 小时前
✨ 索引有哪些缺点以及具体有哪些索引类型
数据库·mysql
利瑞华1 小时前
数据库索引:缺点与类型全解析
数据库·oracle
V+zmm101341 小时前
自驾游拼团小程序的设计与实现(ssm论文源码调试讲解)
java·数据库·微信小程序·小程序·毕业设计