Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何加速这个TSQL查询?_Sql_Sql Server_Performance_Tsql_Sql Server 2016 - Fatal编程技术网

如何加速这个TSQL查询?

如何加速这个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

我有一个运行“慢”的TSQL select查询

我正在以32位SSMS运行查询。 执行时间是17-21秒,我已经测试过在DossierKey和ReportingDate上添加非聚集索引,但这只会减慢查询速度

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