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

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

以下是一个示例查询:

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

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

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone])

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

[ParentStoreId] = 10
AND ([Type] = 0 OR [Type] = 1)

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

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

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
--AND ([Type] = 0 OR [Type] = 1)
ORDER BY [Phone]

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

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

EDIT:

我正在使用的表有2000多万行

推荐答案

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

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

这是一个过于笼统的结论.通常,排序操作符可以很容易地移到索引中,如果只提取结果集的前两行,则可以大大降低查询成本,因为数据库不再需要提取所有匹配的行(并对它们全部排序)来查找第一行,而是可以按结果集顺序读取记录,一旦找到足够的记录就停止.

在您的例子中,您似乎正在获取整个结果集,因此排序不太可能让事情变得更糟(除非结果集很大).此外,在您的情况下,构建一个有用的排序索引可能并不简单,因为where子句包含or.

现在,如果您仍然希望删除该排序操作符,您可以try :

SELECT [Phone]
FROM [dbo].[Store]
WHERE [ParentStoreId] = 10
AND [Type] in (0, 1)
ORDER BY [Phone]    

或者,您可以try 以下索引:

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Phone], [Type])

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

Edit:作为最后的手段,你可以使用

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]

具有

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone])

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

Database相关问答推荐

如何限制报表中返回的行数?

使用 noSQL 和 MongoDB 在数据库中存储任何类型文件的最佳方法是什么

数据库 struct 建议

将一个表中的多个列连接到另一个表中的单个列

在 SQL Server 中以编程方式创建数据库

关闭连接会自动关闭语句和结果集吗?

Android Room持久性库@Update不起作用

在表上插入或更新违反外键约束

Membase 和 Couchbase 有什么区别?

外键可以作为主键吗?

如何使用 MySQL Workbench 更改字段的值?

应用程序用户应该是数据库用户吗?

从数据库行在 Golang 中创建map

哪个更重要?数据库设计或编码?

何时在关系数据库中使用枚举或小表?

我应该混淆用户的数据库 ID 吗?

Codeigniter - 使用多个数据库

在连接表中,Rails 缺少组合键的最佳解决方法是什么?

用于 sql 表中的状态列的类型

DBMS中数据模型和数据库模式的区别?