CROSS APPLY & OUTER APPLY
阿新 • • 發佈:2021-12-31
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 |
---|---|
偶數 | |
奇數 |