当前位置: 首页 > 面试题库 >

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

宁卓
2023-03-14
问题内容

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

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)

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

因此,即使在建立索引时,优化器仍会使用Sort运算符(而不是索引排序),因为它[Phone]是在AFTER
[ParentStoreId]AND之后进行排序的[Type]。如果我[Type]从索引中删除该列并运行查询:

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

然后,当然,优化器不使用Sort运算符,因为[Phone]它由排序[ParentStoreId]

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

编辑:

我正在使用的表有超过2000万行


问题答案:

首先,您应该验证排序实际上是性能瓶颈。排序的持续时间将取决于要排序的元素的数量,并且特定父存储的存储数量可能很小。(这是假定在应用where子句之后应用了sort运算符)。

我听说排序运算符表示查询中的设计不正确,因为可以通过索引过早地进行排序

太笼统了。通常,可以将排序运算符平移到索引中,并且,如果仅获取结果集的前几行,则可以大大降低查询成本,因为数据库不再需要获取所有匹配的行(并对它们进行排序)全部)以查找第一个,但可以按结果集顺序读取记录,并在找到足够的记录后停止。

在您的情况下,您似乎正在获取整个结果集,因此排序不太可能使情况变得更糟(除非结果集很大)。另外,在您的情况下,构建有用的排序索引可能并不容易,因为where子句包含or。

现在,如果您仍然想摆脱该排序运算符,可以尝试:

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

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

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

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

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

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,尤其是考虑到由于索引未聚集,磁盘将执行大量随机访问。



 类似资料:
  • 问题内容: 我有一个具有这样的结构的表: 我想计算给定坐标与数据库中保存的坐标之间的距离。 我当前的查询: 再加上一些的数据。 有什么方法可以优化该查询?进行连接大约需要13毫秒。 我还需要在此处添加一些以及用于分页的商店总数。 问题答案: 这里有一些想法,根据您的实际情况,其中一些想法可能不适用。 您可以将纬度和经度转换为弧度,并将其存储在行中。这将节省这些计算的成本(实际上,在存储数据时,成本

  • 问题内容: 我对执行速度非常慢的存储过程有些困惑。该存储过程基本上包含一个使用传入参数(in_id)的查询,并将其放在游标中,如下所示: 当我获得带有预定义值的SQL查询的执行计划时,使用索引可以得到良好的查询结果。但是,当我从应用程序中调用该过程时,我看到没有索引在使用中,并且该表得到了完整扫描,从而降低了性能。 如果删除WHERE子句的第一部分“(in_id为null)”,则应用程序的性能将再

  • 问题内容: 我在Sql Server 2008上有这样的查询: 在上面看到的选择查询中,SqlServer是否优化查询以免一次又一次地计算DATEADD结果。还是将DATEADD结果存储在临时变量中是我自己的责任吗? 问题答案: 令人惊讶的是,我发现内联使用GETDATE()似乎比预先执行这种类型的计算更有效。 如果您检查这些计划,则中间查询将始终以最低的成本(但并非总是最低的花费时间)出现。当然

  • 例1:输入:nums=[3,4,2]输出:6解释:删除4以获得4点,因此3也被删除。然后,删除2个赚取2分。共获得6分。 以下是如何解决它的解释: 算法 我无法理解这里是如何使用和变量的,以及它是如何解决问题语句的。 你能帮我理解这一点吗。

  • 问题内容: 我有以下表格,分别是BankDetails和Transactiondetails。使用这两个表,我想获得帐户名称的当前余额。 表格: 插入两个表的脚本: 输出将是这样的: 我需要使用以上两个表格输入帐户持有人姓名,帐户编号和当前余额。 下面是我的查询,我想获得优化的查询,即如果可能的话不使用子查询。 注意: 在我的情况下,贷方=添加到帐户中的金额,借方=从帐户中扣除的金额。 对于未遵循

  • 问题内容: 我有一个这样的表: 我在上具有唯一的B树索引。所以,当我执行这样的查询时: 由于存在现有索引,我希望看到具有索引范围扫描并且没有子句排序顺序(或任何其他类型的排序)的查询计划。其实我有查询计划 结果: 如果我从查询中删除,我将无法按所需顺序获取数据(这对我来说很奇怪,因为默认情况下,B树索引会按升序构建行树,并且在这种情况下,应提供与i相同的查询计划想法) 我该如何避免? Oracle