2016-08-23 62 views
1

我想知道是否有人可以阐明为什么SQL Server(2016 RTM在我的情况下,但我怀疑这不是版本特定的)执行这个看似不必要的INNER JOIN。SQL Server查询优化器执行不必要的连接

考虑外键加入了以下两个表:

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也正在对[批处理]表执行INNER JOIN,并且由于外键约束的原因,由于连接而无法从[批处理值]中删除记录。

screenshot of query and execution plan

这在我看来,查询优化器应该丢弃INNER JOIN是不必要的,只是做一个主键征求[batch_values],但事实并非如此。

这是很重要的,因为如果我开发视图连接多个表来呈现底层数据的“更大图片”以便于使用,那么在查询这些视图时,我将会获得性能优势。

+1

行不能丢弃,但它们可以相乘。 – Blorgbeard

+0

哦,不,他们不能,因为你在batch_values上指定了PK。嗯。 – Blorgbeard

+0

哦,是的,他们可以这样做,因为FK只能用一种方式。它可能会从其他表中遗漏。这是一个奇怪的情况,因为它是一个垂直分区表(都加入主键)。如果我以正确的方式读取FK,您可以在'batch'中创建一条记录,而不会以'batch_values'存在(但不是另一种方式) –

回答

0

使用SQL Optimizer的JOIN ELIMINATION有很多限制

E.g.如果您在外键中使用多列,或者约束不受信任,或者标记为“不适用于复制”等。

如果您在外键列中指定WHERE谓词,SQL Server可能不会使用JOIN ELIMINATION。

删除WHERE或删除“BATCH_ID = 100”从那里,你应该看到的优化现在使用连接排除

的文档关于这一主题的限制,所以我不能提供证明的链接,但许多人们在过去的5 - 7年里针对不同的版本报道了这个问题,并且同意这种行为是通过设计的。我的建议是向MS提出一个事件,并直接询问它是否对您的系统至关重要。

+0

我玩了一些,还做了一些搜索JOIN ELIMINATION,看起来你对WHERE子句是正确的。 –