首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >通过删除执行计划中的排序运算符来优化SQL查询

通过删除执行计划中的排序运算符来优化SQL查询
EN

Stack Overflow用户
提问于 2011-05-14 18:23:56
回答 1查看 63.7K关注 0票数 23

我刚刚开始研究如何通过索引优化我的查询,因为SQL数据正在快速增长。我查看了优化器如何通过SSMS中的执行计划处理我的查询,并注意到正在使用排序操作符。我听说排序操作符在查询中表明了一个糟糕的设计,因为通过索引可以过早地进行排序。下面是一个示例表和数据,类似于我正在做的事情:

代码语言:javascript
复制
IF OBJECT_ID('dbo.Store') IS NOT NULL DROP TABLE dbo.[Store]
GO

CREATE TABLE dbo.[Store]
(
    [StoreId] int NOT NULL IDENTITY (1, 1),
    [ParentStoreId] int NULL,
    [Type] int NULL,
    [Phone] char(10) NULL,
    PRIMARY KEY ([StoreId])
)

INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 0, '2223334444')
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 0, '3334445555')
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 1, '0001112222')
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 1, '1112223333')
GO

下面是一个查询示例:

代码语言:javascript
复制
SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND ([Type] = 0 OR [Type] = 1)
ORDER BY [Phone]

我创建了一个非聚集索引来帮助提高查询速度:

代码语言:javascript
复制
CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone])

为了构建IX_Store索引,我从简单的谓词开始

代码语言:javascript
复制
[ParentStoreId] = 10
AND ([Type] = 0 OR [Type] = 1)

然后,我为ORDER BY和添加了[Phone]列,以涵盖SELECT输出

因此,即使构建了索引,优化器仍然使用排序操作符(而不是索引排序),因为[Phone]是在[ParentStoreId][Type]之后排序的。如果我从索引中删除[Type]列并运行查询:

代码语言:javascript
复制
SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
--AND ([Type] = 0 OR [Type] = 1)
ORDER BY [Phone]

当然,优化器不会使用排序操作符,因为[Phone]是按[ParentStoreId]排序的。

因此,问题是如何创建一个覆盖查询(包括[Type]谓词)的索引,而不让优化器使用排序?

编辑:

我正在处理的表有2000多万行

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-05-14 19:09:28

首先,您应该验证排序实际上是一个性能瓶颈。排序的持续时间将取决于要排序的元素的数量,并且特定父商店的商店数量可能很小。(假设排序操作符是在应用where子句之后应用的)。

我听说排序操作符表示查询中的错误设计,因为通过索引可以过早地进行排序

这是一个过于泛化的概念。通常,排序操作符可以简单地移到索引中,如果只提取结果集的前两行,则可以显著降低查询成本,因为数据库不再需要提取所有匹配的行(并对它们进行排序)来查找第一行,而是可以按结果集顺序读取记录,并在找到足够的记录后停止。

在您的例子中,您似乎是在获取整个结果集,所以排序不太可能使事情变得更糟(除非结果集很大)。此外,在您的示例中,构建有用的排序索引可能不是一件容易的事情,因为where子句包含一个或。

现在,如果你仍然想摆脱排序操作符,你可以尝试:

代码语言:javascript
复制
SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] in (0, 1)
ORDER BY [Phone]    

或者,您可以尝试以下索引:

代码语言:javascript
复制
CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Phone], [Type])

为了尝试让查询优化器仅在ParentStoreId上执行索引范围扫描,然后扫描索引中所有匹配的行,如果Type匹配,则输出它们。但是,这可能会导致更多磁盘I/O,从而降低查询速度而不是加快查询速度。

编辑:作为最后的手段,您可以使用

代码语言:javascript
复制
SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] = 0
ORDER BY [Phone]

UNION ALL

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] = 1
ORDER BY [Phone]

使用

代码语言:javascript
复制
CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone])

并在应用服务器上对这两个列表进行排序,您可以在应用服务器上合并(就像在合并排序中一样)预先排序的列表,从而避免完整的排序。但这实际上是一种微优化,虽然将排序速度提高了一个数量级,但不太可能对查询的总执行时间产生太大影响,因为我预计瓶颈将是网络和磁盘I/O,特别是考虑到磁盘将进行大量随机访问,因为索引不是集群的。

票数 19
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6001197

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档