Sql server SQL Server查询优化器正在执行不必要的联接

Sql server SQL Server查询优化器正在执行不必要的联接,sql-server,join,Sql Server,Join,我想知道是否有人能解释一下为什么SQL Server(在我的例子中是2016 RTM,但我怀疑这不是版本特定的)正在执行这种看似不必要的内部连接 考虑以下两个由外键连接的表: 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

我想知道是否有人能解释一下为什么SQL Server(在我的例子中是2016 RTM,但我怀疑这不是版本特定的)正在执行这种看似不必要的内部连接

考虑以下两个由外键连接的表:

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;
设置不计数;
声明
@BatchCount int,
@BatchId smallint,
@密钥计数int;
设置@BatchCount=1;

虽然@BatchCount使用SQL优化器的联接消除有很多限制

例如,如果在外键中使用多列,或约束不受信任,或标记为“不用于复制”,等等

若您使用外键中的列指定WHERE谓词,SQL Server可能不会使用联接消除

从何处删除或从何处删除“Batch_id=100”,您应该会看到优化器现在使用联接消除


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

行不能删除,但可以相乘。哦,不,它们不能,因为您正在指定批处理值的PK。嗯。哦,是的,他们可以,因为FK只能走一条路。另一张桌子上可能没有。这是一个奇怪的情况,因为它是一个垂直分区的表(两者都在主键上连接)。如果我以正确的方式读取FK,您可以在
batch
中创建记录,而不在
batch\u values
中创建记录(但不是另一种方式)。是的,您正在正确读取FK,可以在[batch]表中有一个批次,但在[batch_values]表中没有相应的值。从语义上讲,它意味着一个空批,即批被生成,但结果却是空的。但是,由于[Batchs].[Id]是主键,因此无法通过与[Batchs]的联接来对[batch_value]中的记录进行乘法运算,因此只能有一个。虽然不能进行乘法运算,但可以排除。这是一个内部连接。我想知道一个外部连接是否会有不同的操作。我对它进行了更多的研究,并对连接消除进行了更多的搜索,看起来您对WHERE子句的理解是正确的。