SQL 执行计划

Posted

技术标签:

【中文标题】SQL 执行计划【英文标题】:SQL Execution Plan 【发布时间】:2016-06-12 11:16:39 【问题描述】:

我有两个相似的表,

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;

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

Query1:相对批次:86%

SELECT (COST 0%)

查询 2: 相对于批次:14%

SELECT (Cost0%)

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

【问题讨论】:

【参考方案1】:

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

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

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

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

因此,在计划切换到索引扫描之前的临界点可能是表中非常低的比例。在这种情况下,非覆盖索引可以避免排序这一事实可能会使临界点比其他情况高一点。

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

【讨论】:

以上是关于SQL 执行计划的主要内容,如果未能解决你的问题,请参考以下文章

获取SQL执行计划的常见几种方法

查看SQL的执行计划+执行计划顺序:

怎么使用plsql查看执行计划

Mysql学会查看sql的执行计划

怎么使用plsql查看执行计划

oracle sql的执行计划如何查看