SQL执行计划

标签 sql sql-server

我有两个类似的表,

CREATE TABLE [dbo].[StockPrices] (
    [Id]        INT             IDENTITY (1, 1) NOT NULL,
    [CompanyId] INT             NOT NULL,
    [Date]      DATETIME        NOT NULL,
    [Open]      DECIMAL (18, 2) NOT NULL,
    [Close]     DECIMAL (18, 2) NOT NULL,
    [Low]       DECIMAL (18, 2) NOT NULL,
    [High]      DECIMAL (18, 2) NOT NULL,
    [Volume]    INT             NOT NULL,
    CONSTRAINT [PK_dbo.StockPrices] PRIMARY KEY NONCLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.StockPrices_dbo.Companies_CompanyId] FOREIGN KEY ([CompanyId]) REFERENCES [dbo].[Companies] ([Id]) ON DELETE CASCADE
);

GO
CREATE CLUSTERED INDEX [IX_CompanyId] ON [dbo].[StockPrices]([CompanyId] ASC);

GO
CREATE NONCLUSTERED INDEX [IX_Date] ON [dbo].[StockPrices]([Date] ASC);

CREATE TABLE [dbo].[News] (
    [Id]             INT            IDENTITY (1, 1) NOT NULL,
    [NewsProviderId] INT            NOT NULL,
    [CompanyId]      INT            NOT NULL,
    [Date]           DATETIME       NOT NULL,
    [Title]          NVARCHAR (128) NOT NULL,
    [Description]    NVARCHAR (256) NOT NULL,
    [Url]            NVARCHAR (256) NOT NULL,
    CONSTRAINT [PK_dbo.News] PRIMARY KEY NONCLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.News_dbo.Companies_CompanyId] FOREIGN KEY ([CompanyId]) REFERENCES [dbo].[Companies] ([Id]) ON DELETE CASCADE
);
GO

CREATE CLUSTERED INDEX [IX_CompanyId] ON [dbo].[News]([CompanyId] ASC);
GO

CREATE NONCLUSTERED INDEX [IX_Date] ON [dbo].[News]([Date] ASC);
GO

和两个类似的查询

select *
from news
where companyid = 1
    and date >= '01/01/2010'
    and date <= '01/31/2010'
order by date;

select *
from stockprices
where companyid = 1
    and date >= '01/01/2010'
    and date <= '01/31/2010'
order by date;

我得到了两个完全不同的实际执行计划

查询1:相对于批处理:86%

SELECT (COST 0%) <- Nested Loops (Inneer Join)(Cost 0%) <- Index Seek (NonClustered) [News].[IX_Date](Cost 1%) <- Key Lookup (Clustered) [News].[IX_CompanyId](Cost 99%)

查询2:相对于批处理:14%

SELECT (Cost0%) <- Sort (Cost 33%) <- Clustered Index Scan (Clustered) [StockPrices]IX_CompanyId

我不知道为什么?你能给点建议吗?

最佳答案

第一个是按日期顺序对非覆盖聚集索引进行查找,并使用键查找来获取与 companyid = 1 匹配的行的剩余列。

第二个是对覆盖索引进行扫描,然后对过滤结果进行排序。

这是一个基于成本的决策,取决于估计匹配的表的比例以及两个索引的宽度 ( some example calculations here )。

键查找的成本很高,因为每个键查找都需要执行聚集索引查找来定位相关的页和行。这意味着非聚集索引查找所找到的每个必须读取多个页面(与聚集索引的深度一样多的页面)。此外,为一行找到的聚集索引页很可能与下一行的页不相关,从而需要大量的随机 IO。

因此,计划切换到索引扫描之前的临界点可能只占表的很小一部分。在这种情况下,非覆盖索引可以避免排序,这一事实可能会使临界点比其他情况更高一些。

查看每个的估计行数。另请考虑,News 表包含各种字符串列,并且聚集索引页上的行数可能少于 StockPrices 表中的数值 - 因此,完整的聚集索引扫描新闻很可能相对更昂贵,并导致更高的临界点。

关于SQL执行计划,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37773599/

相关文章:

sql - PostgreSQL 和锁定

sql-server - SQL/DAX 中的多父层次结构传播

sql-server - 将 VARBINARY 的字符串表示形式转换为 VARBINARY 值

sql - 从查询窗口中,可以在另一个查询窗口中打开存储过程吗?

c++ - MS SQL、C++ 解决方案的最佳全文搜索替代方案

mysql - 分组最大值

SQL 分组限制

sql - 从每个类别中至少选择一个但不超过一个,并且没有与另一列重复

mysql - 如何使用 mysql 获取最近 5 天的总计?

sql - 如何使用Group By子句合并两个sql server表?