Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.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
SQL Server查询间歇性性能问题_Sql_Sql Server_Sql Server 2016 - Fatal编程技术网

SQL Server查询间歇性性能问题

SQL Server查询间歇性性能问题,sql,sql-server,sql-server-2016,Sql,Sql Server,Sql Server 2016,最近,我们在SQL Server(2016)上遇到了一个特定查询的性能问题。我看到的问题是,性能问题令人难以置信地不一致,我不确定如何改进 该表详细说明: CREATE TABLE ContactRecord ( ContactSeq BIGINT NOT NULL , ApplicationCd VARCHAR(2) NOT NULL , StartDt DATETIME2 NOT NULL , EndDt DATETIME2 , EndStateCd VARCHAR(3) ,

最近,我们在SQL Server(2016)上遇到了一个特定查询的性能问题。我看到的问题是,性能问题令人难以置信地不一致,我不确定如何改进

该表详细说明:

CREATE TABLE ContactRecord 
(
  ContactSeq BIGINT NOT NULL 
, ApplicationCd VARCHAR(2) NOT NULL 
, StartDt DATETIME2 NOT NULL 
, EndDt DATETIME2 
, EndStateCd VARCHAR(3) 
, UserId VARCHAR(10) 
, UserTypeCd VARCHAR(2) 
, LineId VARCHAR(3) 
, CallingLineId VARCHAR(20) 
, DialledLineId VARCHAR(20) 
, ChannelCd VARCHAR(2) 
, SubChannelCd VARCHAR(2) 
, ServicingAgentCd VARCHAR(7) 
, EucCopyTimestamp VARCHAR(30) 
, PRIMARY KEY (ContactSeq)
, FOREIGN KEY (ApplicationCd) REFERENCES ApplicationType(ApplicationCd)
, FOREIGN KEY (EndStateCd) REFERENCES EndStateType(EndStateCd)
, FOREIGN KEY (UserTypeCd) REFERENCES UserType(UserTypeCd)
)

CREATE TABLE TransactionRecord 
(
  TransactionSeq BIGINT NOT NULL 
, ContactSeq BIGINT NOT NULL 
, TransactionTypeCd VARCHAR(3) NOT NULL 
, TransactionDt DATETIME2 NOT NULL 
, PolicyId VARCHAR(10) 
, ProductId VARCHAR(7) 
, EucCopyTimestamp VARCHAR(30) 
, Detail VARCHAR(1000) 
, PRIMARY KEY (TransactionSeq)
, FOREIGN KEY (ContactSeq) REFERENCES ContactRecord(ContactSeq)
, FOREIGN KEY (TransactionTypeCd) REFERENCES TransactionType(TransactionTypeCd)
)
当前记录计数:

  • ContactRecord
    2000万
  • 交易记录
    9000万
我的问题是:

select
   UserId,
   max(StartDt) as LastLoginDate 
from
   ContactRecord 
where
   ContactSeq in 
   (
      select
         ContactSeq 
      from
         TransactionRecord 
      where
         ContactSeq in 
         (
            select
               ContactSeq 
            from
               ContactRecord 
            where
               UserId in 
               (
                  '1234567890',
                  '1234567891' -- Etc.
               )
         )
         and TransactionRecord.TransactionTypeCd not in 
         (
            '122'
         )
   )
   and ApplicationCd not in 
   (
      '1',
      '4',
      '5'
   )
group by
   UserId;
现在查询不是很好,可以使用连接进行改进,但是它基本上可以工作。 我遇到的问题是,我们的数据作业需要输入大约7100个用户ID。然后将其分成500人一组。对于每500个,它们将用于此查询中的
in
子句中。在子句中的
中,此查询的前14次执行包含500项,执行得很好。结果将在大约15-20秒内返回

问题在于最后一次执行此查询时剩余的100个给定值。它似乎永远不会完成。它只是挂着。在我们的数据作业中,它在10分钟后超时。我不知道为什么。我不是SQL Server的专家,所以我不确定如何调试它。我独立执行了每个子查询,然后用返回的数据替换了子查询的内容。对每个子查询执行此操作效果良好

这里非常感谢您的任何帮助,因为我不知道如何在大量参数下如此一致地工作,但仅在一小部分参数下不工作

编辑

我这里有三个执行计划的例子。请注意,每一个测试都是在测试服务器上执行的,并且几乎都是立即执行的,因为这个测试的数据非常少

这是500个参数的执行计划,在生产中执行良好,大约在15-20秒内返回:

这是119个参数的执行计划,在10分钟后数据作业中超时:

这是5个参数的执行计划,执行良好。此查询未在数据作业中显式执行,只是为了进行比较:

在所有情况下,SSMS均发出以下警告:

/*
Missing Index Details from SQLQuery2.sql
The Query Processor estimates that implementing the following index could improve the query cost by 26.3459%.
*/

/*
USE [CloasIvr]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[TransactionRecord] ([TransactionTypeCd])
INCLUDE ([ContactSeq])
GO
*/
/*
SQLQuery2.sql中缺少索引详细信息
查询处理器估计,实现以下索引可以将查询成本提高26.3459%。
*/
/*
使用[CloasIvr]
去
创建非聚集索引[]
在[dbo].[TransactionRecord]([TransactionTypeCd])
包括([ContactSeq])
去
*/

这是这个问题的根本原因吗?

如果看不到发生了什么,就很难确切地知道发生了什么-尤其是那些失败的。“良好”运行的执行计划可能会有所帮助,但我们只是猜测在糟糕的运行中会出现什么问题

我最初的猜测(类似于我的评论)是,对它的预期的估计是非常错误的,它创建了一个非常糟糕的计划

尤其是TransactionRecord表,其
detail
列为1000个字符,可能会出现大量嵌套循环的大问题

索引

我建议的第一件事是索引——特别是要a)只包含这些数据所需的数据子集,b)以有用的方式对它们进行排序

我认为以下两个索引似乎会有所帮助

CREATE INDEX IX_ContactRecord_User ON ContactRecord 
    (UserId, ContactSeq) 
    INCLUDE (ApplicationCD, Startdt);

CREATE INDEX IX_TransactionRecord_ContactSeq ON TransactionRecord 
    (ContactSeq, TransactionTypeCd);
这些都是“覆盖索引”,也都是以有帮助的方式进行排序的。 或者,您可以用稍微修改过的版本替换第一个版本(在ContactSeq上首先排序),但我认为上面的版本会更有用

CREATE INDEX IX_ContactRecord_User2 ON ContactRecord 
    (ContactSeq) 
    INCLUDE (ApplicationCD, Startdt, UserId);
另外,关于TransactionRecord上的索引-如果这是唯一一个使用该索引的查询,您可以通过创建以下索引来改进它

CREATE INDEX IX_TransactionRecord_ContactSeq_Filtered ON TransactionRecord
    (ContactSeq, TransactionTypeCd) 
    WHERE (TransactionTypeCD <> '122');
然后将查询的中间部分替换为

      where
         ContactSeq in 
         (
            select
               ContactSeq 
            from
               ContactRecord CR
               INNER JOIN #Users U ON CR.UserID = U.UserID 
         )
         and TransactionRecord.TransactionTypeCd not in 
         (
            '122'
         )
简化查询

我尝试过简化查询,结果是:

select  CR.UserId,
        max(CR.StartDt) as LastLoginDate 
from    ContactRecord CR
        INNER JOIN TransactionRecord TR ON CR.ContactSeq = TR.ContactSeq
where   TR.TransactionTypeCd not in ('122')
        AND CR.ApplicationCd not in ('1', '4', '5')
        AND CR.UserId in ('1234567890', '1234567891') -- etc
group by UserId;
或者(使用临时表)

简化查询的一个优点是,它还可以帮助SQLServer获得良好的估计;这反过来有助于它获得良好的执行计划


当然,您需要测试上述返回的记录在您的情况下是否完全相同-我没有要测试的数据集,因此我无法100%确定这些简化版本是否与原始版本匹配。

下载Adam Machanic的“Whoisactive”,并使用它对运行缓慢的查询进行故障排除,或者,您可以获取其中一个“良好”运行的执行计划和另一个较慢运行的执行计划,并将它们上载到“粘贴计划”并更新您的问题。imo很可能,对于较小的数字,它会根据基数估计值(行数估计值)创建不同的执行计划-然后,较小插入的部分计划包括一些低效的内容,例如嵌套循环,或对其中一个表进行多次完整扫描。我建议1轮500,1轮只有5或10(因为100挂起),两者都
设置统计时间,IO打开和显示。此外,如果您需要有关如何改进的帮助,您可以更改什么?是否允许您更改查询、使用临时表和/或向表添加索引?@seanb我将使用实际执行计划重试。在这里用曲线球攻击你的理论;较小列表中的确切项目数为120。这个挂起了,我已经放了20分钟没有结果了。若我从列表中删除13项,它几乎会立即返回(12 less是同一个故事,但任何13都会立即返回。你会期望这样吗?就改进而言,理论上任何事情都可以做,但我感兴趣的是找到导致这种情况似乎永远不会返回的原因。你是否为外键列添加了索引?特别是在将它们与
in
not in条件:与主键约束不同,创建外键约束不会自动创建
select  CR.UserId,
        max(CR.StartDt) as LastLoginDate 
from    ContactRecord CR
        INNER JOIN TransactionRecord TR ON CR.ContactSeq = TR.ContactSeq
where   TR.TransactionTypeCd not in ('122')
        AND CR.ApplicationCd not in ('1', '4', '5')
        AND CR.UserId in ('1234567890', '1234567891') -- etc
group by UserId;
select  CR.UserId,
        max(CR.StartDt) as LastLoginDate 
from    ContactRecord CR
        INNER JOIN #Users U ON CR.UserID = U.UserID 
        INNER JOIN TransactionRecord TR ON CR.ContactSeq = TR.ContactSeq
where   TR.TransactionTypeCd not in ('122')
        AND CR.ApplicationCd not in ('1', '4', '5')
group by UserId;