【发布时间】:2016-08-23 02:30:11
【问题描述】:
我想知道是否有人可以解释为什么 SQL Server(在我的例子中是 2016 RTM,但我怀疑这不是特定于版本)执行这个看似不必要的 INNER JOIN。
考虑以下两个通过外键连接的表:
CREATE TABLE [dbo].[batches](
[Id] [smallint] IDENTITY(1,1) PRIMARY KEY,
[Date] [date] NOT NULL,
[Run] [tinyint] NOT NULL,
[Clean] [bit] NOT NULL)
CREATE TABLE [dbo].[batch_values](
[Batch_Id] [smallint] NOT NULL,
[Key] [int] NOT NULL,
[Value] [int] NOT NULL,
CONSTRAINT [PK_batch_values] PRIMARY KEY CLUSTERED
( [Batch_Id] ASC, [Key] ASC))
GO
ALTER TABLE [dbo].[batch_values] WITH CHECK
ADD CONSTRAINT [FK_batch_values_batches] FOREIGN KEY([Batch_Id])
REFERENCES [dbo].[batches] ([Id])
GO
ALTER TABLE [dbo].[batch_values] CHECK CONSTRAINT [FK_batch_values_batches]
GO
用一些数据填充表格:
SET NOCOUNT ON;
DECLARE
@BatchCount int,
@BatchId smallint,
@KeyCount int;
SET @BatchCount = 1;
WHILE @BatchCount <= 100
BEGIN
INSERT INTO dbo.[batches]
VALUES (DATEADD(dd, @BatchCount / 10, '2016-01-01'), @BatchCount % 10, @BatchCount % 2);
SET @BatchId = SCOPE_IDENTITY();
SET @KeyCount = 1;
WHILE @KeyCount <= 1000
BEGIN
INSERT INTO dbo.batch_values
VALUES (@BatchId, @KeyCount, RAND() * 1000000 - 500000);
SET @KeyCount = @KeyCount + 1;
END;
SET @BatchCount = @BatchCount + 1;
END;
现在,如果我运行以下查询,执行计划显示 SQL Server 正在对 [batches] 表执行 INNER JOIN,即使没有从中选择任何列,也无法从 [batch_values] 中删除任何记录由于外键约束而连接的结果。
screenshot of query and execution plan
在我看来,查询优化器应该丢弃不必要的 INNER JOIN 并简单地在 [batch_values] 上进行主键搜索,但事实并非如此。
这很重要,因为如果我开发连接多个表的视图以呈现基础数据的“更大图景”以便于使用,那么在查询这些视图时我将受到性能影响。
【问题讨论】:
-
行不能被删除,但可以相乘。
-
哦,不,他们不能,因为您在 batch_values 上指定 PK。嗯。
-
哦,是的,他们可以,因为 FK 只走一条路。它可能从另一个表中丢失。这是一个奇怪的情况,因为它是一个垂直分区的表(都连接到主键上)。如果我以正确的方式阅读 FK,您可以在
batch中创建一条记录,而batch_values中不存在该记录(但不是其他方式) -
嗨尼克。是的,您正在正确阅读 FK,可能在 [batches] 表中有一个批次,但在 [batch_values] 表中没有相应的值。从语义上讲,这意味着一个空批次,即批次已生成但结果是空的。但是,无法通过与 [batches] 的连接来将 [batch_value] 中的记录相乘,因为 [batches].[Id] 是主键,因此可能只有一个。
-
不能相乘,但可以排除。这是一个内部连接。我想知道外部联接是否会有所不同
标签: sql-server join