我的数据库中有以下表格。
姓氏表
CREATE TABLE [dbo].[LastNames](
[LastNameID] [int] IDENTITY(1,1) NOT NULL,
[LastName] [varchar](50) NOT NULL
) ON [PRIMARY]
GO
CREATE UNIQUE CLUSTERED INDEX [CIX_LastNames_LastName] ON [dbo].[LastNames]
(
[LastName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
名字表
CREATE TABLE [dbo].[FirstNames](
[FirstNameID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](50) NOT NULL
) ON [PRIMARY]
GO
CREATE UNIQUE CLUSTERED INDEX [CIX_FirstNames_FirstName] ON [dbo].[FirstNames]
(
[FirstName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
人员匿名表
CREATE TABLE [dbo].[PersonsAnon](
[PersonID] [int] IDENTITY(1,1) NOT NULL,
[LastNameID] [int] NOT NULL,
[FirstNameID] [int] NOT NULL,
[Info1] [bit] NULL,
[Info2] [char](1) NULL,
[Info3] [nchar](50) NULL,
[AdressID] [int] NULL
) ON [PRIMARY]
GO
CREATE UNIQUE CLUSTERED INDEX [CIX_PersonsAnon_PersonID_LastNameID_FirstNameID] ON [dbo].[PersonsAnon]
(
[PersonID] ASC,
[LastNameID] ASC,
[FirstNameID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [NIX_PersonsAnon_LastNameID_FirstNameID] ON [dbo].[PersonsAnon]
(
[LastNameID] ASC,
[FirstNameID] ASC
)
INCLUDE([PersonID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
数据生成
对于那些拥有 AdventureWorks2014 数据库的人,我使用以下语句来填充表:
INSERT INTO dbo.LastNames (LastName) SELECT DISTINCT app.LastName FROM AdventureWorks2016.Person.Person AS app
INSERT INTO dbo.FirstNames (FirstName) SELECT DISTINCT app.FirstName FROM AdventureWorks2016.Person.Person AS app
INSERT INTO dbo.PersonsAnon (LastNameID, FirstNameID)
SELECT ln.LastNameID, fn.FirstNameID FROM LastNames ln CROSS APPLY FirstNames fn
该声明
使用以下语句来查看将创建什么样的执行计划:
SELECT pa.PersonID,
fn.FirstName,
ln.LastName,
pa.Info2
FROM PersonsAnon AS pa
JOIN LastNames AS ln
ON pa.LastNameID = ln.LastNameID
JOIN FirstNames AS fn
ON pa.FirstNameID = fn.FirstNameID
WHERE ln.LastName LIKE 'Pet%'
AND fn.FirstName LIKE 'John%'
ORDER BY
ln.LastName,
fn.FirstName;
查询执行计划
问题
为什么即使没有 JOIN 谓词,也要在 LastNames 和 FirstNames 之间执行 JOIN?是什么导致 QO JOIN 这两个表?
图解情况
它看起来是这样的:
细节显示:
优化器确定您的查询是对事实表和维度表的选择性星形查询。
StarJoinInfo
赠品是计划的连接运算符上存在属性(不幸的是,笛卡尔积除外)。从我的文章《执行计划中的 StarJoinInfo》中:可用的策略之一是“笛卡尔积加多列索引查找”。这个想法是对每个维度表应用一个单独的谓词,获取结果的笛卡尔积,并使用它来查找事实表上多列索引的两个键。然后,查询计划使用先前操作中的行标识符对事实表执行查找(RID 或键)。
换句话说:
如果您遵循该逻辑,您将看到此策略返回查询规范所要求的结果。