1. 程式人生 > 其它 >CROSS APPLY & OUTER APPLY

CROSS APPLY & OUTER APPLY

CROSS APPLY & OUTER APPLY

CREATE TABLE [dbo].[Account](
	[UserId] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](50) NULL,
	[Age] [int] NULL,
	[Gender] [int] NULL,
	[BirthDate] [datetime] NULL,
 CONSTRAINT [PK_Account] PRIMARY KEY CLUSTERED 
(
	[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT INTO [dbo].[Account]([UserId], [Name], [Age], [Gender], [BirthDate]) VALUES (1, N'User01', 21, 1, '2000-11-02 15:12:30.000');
INSERT INTO [dbo].[Account]([UserId], [Name], [Age], [Gender], [BirthDate]) VALUES (2, N'User02', 19, 0, '2002-02-02 15:12:30.000');
INSERT INTO [dbo].[Account]([UserId], [Name], [Age], [Gender], [BirthDate]) VALUES (3, N'User03', 20, 1, '2001-03-02 15:12:30.000');
GO
CREATE TABLE [dbo].[Message](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[UserId] [int] NULL,
	[Message] [nvarchar](200) NULL,
	[IsRead] [bit] NULL,
	[CreateTime] [datetime] NULL,
 CONSTRAINT [PK_Message] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT INTO [dbo].[Message]([Id], [UserId], [Message], [IsRead], [CreateTime]) VALUES (1, 1, N'Hello!', '0', '2021-11-28 15:16:14.000');
INSERT INTO [dbo].[Message]([Id], [UserId], [Message], [IsRead], [CreateTime]) VALUES (2, 1, N'First Message', '0', '2021-11-28 15:17:07.000');
INSERT INTO [dbo].[Message]([Id], [UserId], [Message], [IsRead], [CreateTime]) VALUES (3, 1, N'Second Message', '0', '2021-11-28 15:17:32.000');
INSERT INTO [dbo].[Message]([Id], [UserId], [Message], [IsRead], [CreateTime]) VALUES (4, 3, N'Hello!', '0', '2021-11-27 15:17:51.000');
GO

INNER JOIN

內連結,左右兩表中交叉存在的交集記錄.不會出現NULL

SELECT msg.Id,ac.Name,msg.Message,msg.IsRead,msg.CreateTime FROM dbo.Message AS msg INNER JOIN dbo.Account AS ac ON ac.UserId =msg.UserId
Id Name Message IsRead CreateTime
1 User01 Hello! 0 2021-11-28 15:16
2 User01 First Message 0 2021-11-28 15:17
3 User01 Second Message 0 2021-11-28 15:17
4 User03 Hello! 0 2021-11-27 15:17

LEFT JOIN

左側為主表,進行匹配.如果右側沒有,則顯示NULL

SELECT msg.Id,ac.Name,msg.Message,msg.IsRead,msg.CreateTime FROM dbo.Message AS msg LEFT JOIN dbo.Account AS ac ON ac.UserId =msg.UserId
Id Name Message IsRead CreateTime
1 User01 Hello! 0 2021-11-28 15:16
2 User01 First Message 0 2021-11-28 15:17
3 User01 Second Message 0 2021-11-28 15:17
4 User03 Hello! 0 2021-11-27 15:17

OUTER JOIN

RIGHT OUTER JOIN:
右側表為主表,進行匹配.如果左側沒有,則顯示NULL.
查詢結果為右表的並集.

LEFT OUTER JOIN:
左側表為主表,查詢結果為左表的並集.

SELECT msg.Id,ac.Name,msg.Message,msg.IsRead,msg.CreateTime FROM dbo.Message AS msg RIGHT OUTER JOIN dbo.Account AS ac ON ac.UserId =msg.UserId
Id Name Message IsRead CreateTime
1 User01 Hello! 0 2021-11-28 15:16
2 User01 First Message 0 2021-11-28 15:17
3 User01 Second Message 0 2021-11-28 15:17
NULL User02 NULL NULL NULL
4 User03 Hello! 0 2021-11-27 15:17

CROSS APPLY

如果不考慮外部表中的聚合函式.整體實現效果與INNER JOIN差不多.

通過連結查詢,優先進行left外部表計算,然後在保持left外部表所有資料行的基礎上,對right表進行查詢匹配.

CROSS APPLY 可以根據當前左表的當前記錄去查詢右表;

INNER JOIN 是根據左表的當前記錄匹配右表整個結果集;

執行查詢/運算的順序上,比INNER JOIN更嚴格.

查詢結果與INNER JOIN一致,為左右兩表的交集.

SELECT msg.Id,ac.Name,msg.Message,msg.IsRead,msg.CreateTime FROM dbo.Account AS ac CROSS APPLY (SELECT TOP (2) * FROM dbo.Message  WHERE ac.UserId=UserId ORDER BY CreateTime DESC) msg
Id Name Message IsRead CreateTime
3 User01 Second Message 0 2021-11-28 15:17
2 User01 First Message 0 2021-11-28 15:17
4 User03 Hello! 0 2021-11-27 15:17

OUTER APPLY

外聯結,如果不考慮外部表中的聚合函式,與OUTER JOIN差不過.

與OUTER JOIN的區別在於,OUTER APPLY的左右表執行/運算順序.

OUTER APPLY 可以根據當前左表的當前記錄去查詢右表;

INNER JOIN 是根據左表的當前記錄匹配右表整個結果集;

查詢結果與OUTER JOIN一致,為左/右表的並集.

SELECT msg.Id,ac.Name,msg.Message,msg.IsRead,msg.CreateTime FROM dbo.Account AS ac OUTER APPLY (SELECT TOP (2) * FROM dbo.Message  WHERE ac.UserId=UserId ORDER BY CreateTime DESC) msg
Id Name Message IsRead CreateTime
3 User01 Second Message 0 2021-11-28 15:17
2 User01 First Message 0 2021-11-28 15:17
NULL User02 NULL NULL NULL
4 User03 Hello! 0 2021-11-27 15:17

OUTER APPLY

運算測試1

測試案例

WITH m AS (
    SELECT
	'數字' TypeGroup,
	'1,2,3,4,5,6,7,8,9' info UNION ALL
SELECT
	'字母' TypeGroup,
	'a,b,c,d,e,f,g,h,i' info
)
SELECT * FROM m;
TypeGroup info
數字 1,2,3,4,5,6,7,8,9
字母 a,b,c,d,e,f,g,h,i
WITH m AS (
    SELECT
	'數字' TypeGroup,
	'1,2,3,4,5,6,7,8,9' info UNION ALL
SELECT
	'字母' TypeGroup,
	'a,b,c,d,e,f,g,h,i' info
)
-- SELECT * FROM m;
SELECT A.TypeGroup,B.info
FROM(  
    select TypeGroup, CONVERT(xml,'<root><v>' + REPLACE(info, ',', '</v><v>') + '</v></root>')  as info
    from m
) A
OUTER APPLY(
    SELECT info = N.c.value('.', 'varchar(8000)')
    FROM A.info.nodes('/root/v') N(c)
) B
TypeGroup info
數字 1
數字 2
數字 3
數字 4
數字 5
數字 6
數字 7
數字 8
數字 9
字母 a
字母 b
字母 c
字母 d
字母 e
字母 f
字母 g
字母 h
字母 i

測試運算2

WITH Z AS (
SELECT '奇數' [Type],'1' [Value] UNION ALL
SELECT '奇數' [Type],'3' [Value] UNION ALL
SELECT '奇數' [Type],'5' [Value] UNION ALL
SELECT '奇數' [Type],'7' [Value] UNION ALL
SELECT '偶數' [Type],'2' [Value] UNION ALL
SELECT '偶數' [Type],'4' [Value] UNION ALL
SELECT '偶數' [Type],'6' [Value] UNION ALL
SELECT '偶數' [Type],'8' [Value]
)
SELECT * FROM 
(SELECT DISTINCT [Z].[Type] FROM Z) A 
OUTER APPLY (
SELECT [VALUES]=CAST((SELECT [Z].[Value] FROM Z WHERE Z.[Type]=A.[Type] FOR XML AUTO) AS NVARCHAR(MAX))
) B
Type VALUES
偶數
奇數