如何加速这个TSQL查询?
我有一个运行“慢”的TSQL select查询 我正在以32位SSMS运行查询。 执行时间是17-21秒,我已经测试过在DossierKey和ReportingDate上添加非聚集索引,但这只会减慢查询速度如何加速这个TSQL查询?,sql,sql-server,performance,tsql,sql-server-2016,Sql,Sql Server,Performance,Tsql,Sql Server 2016,我有一个运行“慢”的TSQL select查询 我正在以32位SSMS运行查询。 执行时间是17-21秒,我已经测试过在DossierKey和ReportingDate上添加非聚集索引,但这只会减慢查询速度 CREATE UNIQUE NONCLUSTERED INDEX [NCI_InvcNbr] ON [dbo].[SalesCombined] ( [SalesDate] ASC, [Country] ASC, [CompanyName] ASC, [Part
CREATE UNIQUE NONCLUSTERED INDEX [NCI_InvcNbr] ON [dbo].[SalesCombined]
(
[SalesDate] ASC,
[Country] ASC,
[CompanyName] ASC,
[PartNumber] ASC,
[IdentityID] ASC
)
INCLUDE [InvoiceNumber],
[City],
[Region],
[ExtPrice]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
该表有大约6.04M条记录,该结果集将返回1M条记录。
它在SQL 2016开发者版上运行。
服务器规格:8core 16gb ram和HDD=>虚拟服务器
从执行计划来看,我找不到任何改进。
我如何加速?更多的硬件?但我认为这不会有什么帮助,因为在运行此查询时,服务器没有被完全使用
编辑:
执行计划:
索引:
CREATE NONCLUSTERED INDEX [_dx1]
ON [dbo].[FactInvoices] ([DossierKey],[ReportingDate])
INCLUDE ([CustomerKey],[ProductKey],[ReportingDateKey],[RepresentativeKey],[InvoiceQuantity],[InvoiceQuantityKg],[BrutoInvoiceLineAmount],[NettoInvoiceLineAmount],[MarginAmount],[EndOfYearDiscount],[TotalLineCostPrice])
谢谢。对于此查询:
SELECT CustomerKey, ProductKey, RepresentativeKey, ReportingDateKey,
. . .
FROM FactInvoices i
WHERE i.DossierKey = 2 AND
i.ReportingDate BETWEEN '2016-01-01' AND '2017-12-31'
GROUP BY CustomerKey, ProductKey, RepresentativeKey, ReportingDateKey;
我建议在
事实发票(档案、报告日期、客户密钥、产品密钥、代表性密钥)上建立一个索引。前两个是用于WHERE
子句的索引的主要元素。其余三列可能对聚合有用。您还可以包括查询中使用的所有附加列。如果您已经为此查询编制了索引,但它的性能仍然很差,则可以尝试按档案对表进行分区
CREATE UNIQUE NONCLUSTERED INDEX [NCI_InvcNbr] ON [dbo].[SalesCombined]
(
[SalesDate] ASC,
[Country] ASC,
[CompanyName] ASC,
[PartNumber] ASC,
[IdentityID] ASC
)
INCLUDE [InvoiceNumber],
[City],
[Region],
[ExtPrice]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
改变
WHERE i.DossierKey = 2
到
这是我写的一篇关于加速查询的文章
如果查询速度较慢,可以检查执行计划中可能存在的加速区域
嗯,我已经这样做了,并且发现它并不总是有帮助的。同样的执行计划可能需要几秒钟的时间运行或进入“永不着陆”状态,并在7分钟后被杀死
我最近解决了这个问题,使用了各种各样的技术,我以前从未在一个地方提到过,我想在同样的情况下帮助其他人。溶液通常在2秒内返回
这就是我所做的
开始查询
这是一个相当基本的查询。它报告销售订单,并允许用户指定多达6个可选的where条件
•如果用户未输入值的标准,例如国家,则其标准字符串将设置为“”,并且未选中国家
•如果用户确实输入了值的条件,则其条件字符串用“%”括起来。例如,如果用户输入“Tin”,则strCountry设置为“%Tin%”,并选择名称中包含“Tin”的所有国家/地区。(比如阿根廷和马提尼克岛。)
我从我工作的一个数据仓库中获取这个。完整查询中有260000条记录。我们将返回的记录限制为1000条,因为用户永远不会想要更多的记录
有时查询需要10秒或更短的时间,有时我们必须在超过7分钟后终止查询。用户不会等待7分钟
我们想到了什么
有不同的技术可以加速查询。下面是我们的查询结果。我将介绍下面使用的每一种技术
这个新查询通常在2秒钟或更短的时间内返回结果
SELECT
InvoiceNumber
,Company
,Street
,City
,Region
,Country
,SalesDate
,InvoiceTotal
,LineItemNbr
,PartNumber
,Quantity
,UnitPrice
,ExtPrice
,UnitWeight
,ExtWeight
FROM
(
SELECT top 1000
IdentityID,
ROW_NUMBER() OVER (ORDER BY [SalesDate], [Country], [Company], [PartNumber]) as RowNbr
FROM dbo.SalesCombined with(index(NCI_SalesDt))
where
(@strCountry = '' or Country like @strCountry)
and
(@strCompany = '' or Company like @strCompany)
and
(@strPartNumber = '' or PartNumber like @strPartNumber)
and
(@strInvoiceNumber = '' or InvoiceNumber like @strInvoiceNumber)
and
(@strRegion = '' or Region like @strRegion)
and
(@mnyExtPrice = 0 or ExtPrice > @mnyExtPrice)
) SubSelect
Inner Join dbo.SalesCombined on SubSelect.IdentityID = SalesCombined.IdentityID
Order By
RowNbr
技术1-数据非规范化。
我在两方面很幸运:
•数据小到足以创建第二个副本
•数据没有经常变化。这意味着我可以构造为查询而优化的第二个副本,并允许更新需要一段时间
SalesHeader、SalesLineItem和PartMaster表合并到单个SalesCombined表中
计算值也存储在SalesCombined表中
请注意,我保留了原始表。更新这些表的所有代码仍然有效。我必须创建额外的代码,然后将更改传播到SalesCombined表
技术2-创建整数标识值
此非规范化表的第一个字段是整数标识值。这被称为IdentityID
即使我们没有对数据进行反规范化,SalesHeader中的整数标识值也可以用于它与SalesLineItem之间的连接,从而使原始查询的速度提高了一点
技术3-在此整数标识值上创建聚集索引
我在这个IdentityID值上创建了一个聚集索引。这是找到记录的最快方法
技术4-在排序字段上创建唯一的非聚集索引
查询的输出按四个字段排序:SalesDate、Country、Company和PartNumber。因此,我在这些字段SalesDate、Country、Company和PartNumber上创建了一个索引
然后我将IdentityID添加到这个索引中。该索引被认为是唯一的。这允许SQLServer从排序字段快速转到实际记录的地址
技术5:在非聚集索引中包括所有“Where子句”字段
SQL Server索引可以包含不属于排序的字段。(谁想到的?这是个好主意。)如果在索引中包含所有where子句字段,SQL Server就不必查找实际记录来获取此数据
这是正常的查找过程:
1) 从磁盘读取索引。
2) 转到索引上的第一个条目。
3) 从该条目中查找第一条记录的地址。
4) 从磁盘读取那个记录。
5) 查找属于where子句的所有字段并应用条件。
6) 确定该记录是否包含在查询中
CREATE UNIQUE NONCLUSTERED INDEX [NCI_InvcNbr] ON [dbo].[SalesCombined]
(
[SalesDate] ASC,
[Country] ASC,
[CompanyName] ASC,
[PartNumber] ASC,
[IdentityID] ASC
)
INCLUDE [InvoiceNumber],
[City],
[Region],
[ExtPrice]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
如果索引中包含where子句字段:
1) 从磁盘读取索引。
2) 转到索引上的第一个条目。
3) 查找属于where子句(存储在索引中)的所有字段并应用条件。
4) 确定该记录是否包含在查询中
CREATE UNIQUE NONCLUSTERED INDEX [NCI_InvcNbr] ON [dbo].[SalesCombined]
(
[SalesDate] ASC,
[Country] ASC,
[CompanyName] ASC,
[PartNumber] ASC,
[IdentityID] ASC
)
INCLUDE [InvoiceNumber],
[City],
[Region],
[ExtPrice]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
原始查询的执行计划
我们最终查询的执行计划要简单得多——开始时,它只读取索引
技术6:创建子查询以查找标识i