优化SQL查询,TSQL

优化SQL查询,TSQL,sql,sql-server,tsql,query-optimization,Sql,Sql Server,Tsql,Query Optimization,我是一名软件开发人员,最近DBA找我来优化我的一个应用程序正在使用的查询。DBA报告说,查询在运行时占用大约50%的CPU和高I/O操作。这个查询非常直截了当,我不确定如何优化它 问题1:如何优化此查询 问题2:这样做是否是我的工作,DBA是否应该在这方面有更多的知识?请注意,我们没有数据库开发人员,只有DBA和软件开发人员 DB有大约3000-5000万条记录,由DBA持续维护/监控,但我不确定如何维护。服务器位于专用计算机上,并且是Microsoft SQL Server 2005-9.00

我是一名软件开发人员,最近DBA找我来优化我的一个应用程序正在使用的查询。DBA报告说,查询在运行时占用大约50%的CPU和高I/O操作。这个查询非常直截了当,我不确定如何优化它

问题1:如何优化此查询

问题2:这样做是否是我的工作,DBA是否应该在这方面有更多的知识?请注意,我们没有数据库开发人员,只有DBA和软件开发人员

DB有大约3000-5000万条记录,由DBA持续维护/监控,但我不确定如何维护。服务器位于专用计算机上,并且是Microsoft SQL Server 2005-9.00.5057.00(X64)

PS:请不要提供通过结构更改来改进数据库的方法,我知道将货币存储为varchar是一个糟糕的设计,但事实就是这样,我们不能更改数据库结构,只能查询访问它

谢谢你的见解

查询:

SELECT
    COALESCE(CAST([PH].[PAmount] AS decimal(15, 2)) + CAST([PH].[Fee] AS decimal(15, 2)), 0.0) AS [PayAmount],
    [PH].[PDate] AS [PayDate]
FROM [History] AS [PH] WITH (NOLOCK)
WHERE [PH].[PMode] IN ('C', 'P')
    AND [PH].[INNO] = 'XYZ'
    AND [PH].[PStatus] IN ('CONSERVED', 'EXPECTING', 'REFRIGERATED', 'POSTPONED', 'FILED')
    AND [PH].[Locked] = 1
    AND [PH].[PDate] >= 'Jan 1, 2015'
ORDER BY [PH].[PDate] ASC
字段:

SELECT
    COALESCE(CAST([PH].[PAmount] AS decimal(15, 2)) + CAST([PH].[Fee] AS decimal(15, 2)), 0.0) AS [PayAmount],
    [PH].[PDate] AS [PayDate]
FROM [History] AS [PH] WITH (NOLOCK)
WHERE [PH].[PMode] IN ('C', 'P')
    AND [PH].[INNO] = 'XYZ'
    AND [PH].[PStatus] IN ('CONSERVED', 'EXPECTING', 'REFRIGERATED', 'POSTPONED', 'FILED')
    AND [PH].[Locked] = 1
    AND [PH].[PDate] >= 'Jan 1, 2015'
ORDER BY [PH].[PDate] ASC
PAmount
-非聚集索引,
varchar(50)

费用
-未编制索引,
十进制(6,2)

PDate
-聚集索引,
datetime

PMode
-非聚集索引,
varchar(5)

INNO
-非聚集索引,
varchar(50)

PStatus
-非聚集索引,
varchar(50)

锁定
-未索引,

执行计划:

选择---Compute Scalar---Filter---NestedLoops-|--Index Seek
(内部连接)|
成本0%成本0%成本0%成本0%成本0%|成本4%
|---键查找
成本96%

您是对的,查询看起来很正常。这是一个直截了当的查询,只有'AND'子句,没有“NOTNULL”约束或JOIN或subselect。条件基本相同(只有日期是相关的)。如果条件中的值(如“C”、“P”、“1”、“XYZ”、“conservated”等)具有足够的选择性,那么您(或DBA)应该定义一些索引,优化器可以使用它。请DBA为表创建适当的索引


你希望得到多少条结果线?如果有很多(例如>>10000),ORDER BY子句可能会花费很多。

我会看看是否使用ISNULL而不是COALESCE获得了更好的结果

另一件事是查看索引。您列出了索引的字段。如果这些字段被多个索引覆盖,我建议为这个查询创建一个良好的覆盖索引

覆盖索引是指查询所需的所有数据都包含在索引中的索引。如果查询所使用的索引没有覆盖,则会有一个或多个到表的行程来获取其余字段。如果所有数据都在查询中,则效率更高

请阅读以下文章:

对于不属于联接或where子句的数据,可以使用include关键字。包含的字段不是索引的可搜索部分,但会将行程保存到数据库

试试下面的索引。where子句中的所有字段都是索引的可搜索部分的一部分,并且包含所有不属于where子句的返回字段。在看了执行计划后,你可能需要玩弄订单,但我猜得很准

Create Nonclustered Index Ix_Ncl_History_CoveringBigSelect on History(PDate, PMode, INNO, PStatus, Locked) Include (PAmount, Fee)

这是一篇关于包含列的文章。

您似乎对索引有误解。索引彼此不合并,因此这不是一个列“已索引”或“未索引”的问题。为各个列单独设置索引是不好的。它是关于有多个列的索引,这与单个查询有关。如果数据库首先在另一列上进行选择更有效,则列上的索引对查询没有帮助

我对此有点陈词滥调,但对于这个查询,我建议使用一个如下所示的索引:

CREATE NONCLUSTERED INDEX [ix_History_XXXXX] ON [History] 
(
    [INNO] ASC,
    [Locked] ASC,
    [PDate] ASC,
    [PMode] ASC
)
INCLUDE ( PStatus, PAmount, Fee)
您可能需要交换PDate、PMode和PStatus,这取决于它们的不同

建立索引时,首先要列出最具体的项目。一般的想法是索引按顺序存储每个连续项。有了这个索引,
INNO
的所有
XYZ
值的行将被分组在一起,因此查询引擎可以搜索索引的该部分。下一个最具体的列是
Locked
。即使这是一个
值,因为它仅限于一个值,我们仍然能够直接搜索索引中对整个查询有影响的一个特定部分。再说一次:我已经有一段时间没有做这种事情了,所以你可以做这里列出的
PMode
;我不记得Sql Server查询优化器是否足够聪明,能够有效地处理这两个值

从这里开始,索引的最佳选项取决于每个查询值对结果的限制程度。由于我们不再能够将所有结果放在一个空间中,我们必须扫描索引的相关部分。我的直觉是接下来使用
Date
值。这将允许扫描从与结果匹配的第一个日期开始遍历索引,并帮助它以正确的顺序获取记录,但这只是我的直觉。首先列出PMode或PStatus可能会做得更好

最后,
INCLUDES
子句中的附加语句将允许您从索引中完全完成此查询,而无需实际返回完整表。您可以使用INCLUDES子句,而不仅仅是将值附加到