如何提高下面提到的 SQL 查询中的文本列性能

Posted

技术标签:

【中文标题】如何提高下面提到的 SQL 查询中的文本列性能【英文标题】:How to improve the text column performance in below mentioned SQL query 【发布时间】:2017-08-14 10:58:40 【问题描述】:

有一个 SQL union all 查询和 3 个 union all 查询。在查询中添加文本列CAST(c.getQuestionId AS VARCHAR(300)) 后,查询执行时间发生了显着变化。使用的数据库是SQL SERVER 2014,请性能调优高手帮忙。

JcccustomersAssessmentProxy 上创建了 3 个索引。这是一个包含 8000 万条记录的表。仅在第三个查询中提到显式使用索引 (即(NOLOCK, INDEX=IX_AssessmentProxy_myJcAssessmentContext))

myJcAssessmentContext 上有一个非聚集索引

getmycoordtoyplanver 上有一个非聚集索引

getMyEventItem 上有一个非聚集索引

 SELECT     ass.P_KEY
            ,ass.SS_CODE
            ,CAST(evp.EVENT_SSID AS VARCHAR(11)) AS EVENT_SSID
            ,CAST(QUESTIONNUMBER AS VARCHAR(3))                             AS EVENT_NO
            ,CAST(ISNULL(CAST(TEMPLATEVERSION AS VARCHAR), 0) AS INT)       AS TEMPLATEVERSION
            ,ISNULL(CAST(TEMPLATENAME AS VARCHAR(50)), ' ')                 AS TEMPLATENAME
            ,evp.ASS_DATE
            ,ISNULL(ANSWERNUMBER, 0)                                        AS ANSWER_NO
            ,ass.CASE_SSID
            ,ass.RE_SSID
            ,ass.RE_DATE
            ,ass.EPISODE_SSID
            ,ass.[SERVICE]
            ,ass.SERVICE_DESC
            ,ass.TAM_KEY 
            ,ass.PRv_KEY
            ,CAST(QUESTIONNUMBER AS VARCHAR(3))                 AS QUESTION_NO
            ,ISNULL(CONVERT(VARCHAR(100), REPLACE(REPLACE(CAST(QUESTIONTEXT AS VARCHAR(650)), char(10), ''), char(13), ' ')), ' ') AS QUESTION_TEXT
            ,ISNULL(CAST(ANSWER  AS VARCHAR(125)), ' ')         AS ANSWER_TEXT
            ,a.MyAssessment                                     AS ASSESSMENT_SSID
-----------------------------------------------------------------------------------------

            ,CAST(RIGHT(a.getMyCoordtoyPlanVer, 10) AS INT)     AS toy_PLAN_VERSION_SSID    
            ,CAST(RIGHT(a.myJcAssessmentContext, 10) AS INT)        AS CONTEXT_FORM_SSID
-----------------------------------------------------------------------------------------           
            ,ass.RECORD_DATE
                                 **,CAST(a.getQuestionId AS VARCHAR(300) )                    AS QUESTION_ID  /*NEW Text Column ,this remove the parallelism*/**
FROM        SourceFeed.dbo.JcccustomersAssessmentProxy  AS a WITH(NOLOCK)
            INNER JOIN
            TEMP_DATABASE.dbo.jsystemReport_AllEventItems_AllPersons    evp WITH(NOLOCK)
            ON a.getMyEventItem = evp.oid 


            INNER JOIN
            TEMP_DATABASE.dbo.jsystemAssessment                     ass WITH(NOLOCK)
            ON evp.getcustomersId = ass.customersID
            AND evp.EVENT_SSID = ass.EVENTID


UNION ALL

SELECT      ass.P_KEY
            ,ass.SS_CODE
            ,CAST(csp.ENT_SSID AS VARCHAR(11)) AS EVENT_SSID
            ,CAST(jcc.QUESTIONNUMBER AS VARCHAR(3)) AS  EVENT_NO
            ,CAST(ISNULL(CAST(CAST(jcc.TEMPLATEVERSION AS VARCHAR) AS VARCHAR), 0) AS INT) AS TEMPLATEVERSION
            ,ISNULL(CAST(jcc.TEMPLATENAME AS VARCHAR(50)), ' ') AS TEMPLATENAME
            ,csp.ASSESSMENT_DATE
            ,ISNULL(jcc.ANSWERNUMBER, 0) AS ANSWER_NO
            ,ass.CASE_SSID
            ,ass.REF_SSID
            ,ass.RE_DATE
            ,ass.EPISODE_SSID
            ,ass.[SERVICE]
            ,ass.SERVICE_DESC
            ,ass.TEAM_KEY 
            ,ass.PROVIDER_KEY
            ,CAST(ISNULL(jcc.QUESTIONNUMBER, 0) AS VARCHAR(3))  AS QUESTION_NO
            ,isnull(CONVERT(VARCHAR(100), replace(replace(CAST(jcc.QUESTIONTEXT AS VARCHAR(650)), char(10), ''), char(13), ' ')), ' ') AS QUESTION_TEXT
            ,ISNULL(CAST(jcc.ANSWER  AS VARCHAR(125)), ' ') AS ANSWER_TEXT
            ,jcc.MyAssessment AS ASSESSMENT_SSID
-----------------------------------------------------------------------------------------

            ,CAST(RIGHT(jcc.getMyCoordtoyPlanVer, 10) AS INT)       AS toy_PLAN_VERSION_SSID    
            ,CAST(RIGHT(jcc.myJcAssessmentContext, 10) AS INT)      AS CONTEXT_FORM_SSID
-----------------------------------------------------------------------------------------           
            ,ass.RECORD_DATE
                                 **,CAST(jcc.getQuestionId AS VARCHAR(300) )                    AS QUESTION_ID  /*NEW Text Column ,this remove the parallelism*/**
FROM        TEMP_DATABASE.dbo.jsystemReport_toySpell                csp WITH(NOLOCK)
            INNER JOIN
            TEMP_DATABASE.dbo.jsystemAssessment                     ass WITH(NOLOCK)
            ON csp.getcustomersId = ass.customersID
            AND csp.EVENT_SSID = ass.EVENTID
            LEFT OUTER JOIN
            SourceFeed.dbo.JcccustomersAssessmentProxy  jcc WITH(NOLOCK)
            ON csp.OID = jcc.getmycoordtoyplanver

WHERE       NOT EXISTS (SELECT  tp1.OID
                        FROM    TEMP_DATABASE.dbo.AssessmentTransferPart1   tp1
                        WHERE   tp1.OID = jcc.OID)

UNION ALL

SELECT      ass.P_KEY
            ,ass.SS_CODE
            ,CAST(a.EVENT_SSID AS VARCHAR(11)) AS EVENT_SSID
            ,CAST(CAST(C.QUESTIONNUMBER AS INT) AS CHAR(3)) AS EVENT_NO
            ,CAST(ISNULL(CAST(C.TEMPLATEVERSION AS VARCHAR), 0) AS INT) AS TEMPLATEVERSION
            ,ISNULL(CAST(C.TEMPLATENAME AS VARCHAR(50)), ' ') AS TEMPLATENAME
            ,a.ASSESSMENT_DATE
            ,ISNULL(C.ANSWERNUMBER, 0) AS ANSWER_NO
            ,ass.CASE_SSID
            ,ass.REL_SSID
            ,ass.REAL_DATE
            ,ass.EPISODE_SSID
            ,ass.[SERVICE]
            ,ass.SERVICE_DESC
            ,ass.TEAM_KEY 
            ,ass.PROVIDER_KEY
            ,CAST(ISNULL(CAST(C.QUESTIONNUMBER AS INT), 0) AS VARCHAR(3))  AS QUESTION_NO
            ,isnull(CONVERT(VARCHAR(100), CAST(C.QUESTIONTEXT AS VARCHAR(650))), ' ') AS QUESTION_TEXT
            ,ISNULL(CAST(C.ANSWER  AS VARCHAR(125)), ' ') AS ANSWER_TEXT
            ,a.MyAssessment AS ASSESSMENT_SSID
-----------------------------------------------------------------------------------------

            ,CAST(RIGHT(C.getMyCoordtoyPlanVer, 10) AS INT)     AS toy_PLAN_VERSION_SSID    
            ,CAST(RIGHT(C.myJcAssessmentContext, 10) AS INT)        AS CONTEXT_FORM_SSID
-----------------------------------------------------------------------------------------           
            ,ass.RECORD_DATE
                                 **,CAST(c.getQuestionId AS VARCHAR(300) )**                    AS QUESTION_ID  /*NEW Text Column ,this remove the parallelism*/
FROM        SourceFeed.dbo.toyClusterReviewAssessment       B WITH(NOLOCK)
            INNER JOIN
            SourceFeed.dbo.JcccustomersAssessmentProxy      C WITH(NOLOCK, INDEX=IX_AssessmentProxy_myJcAssessmentContext)
            ON B.myJCJccUserFormContext = C.myJcAssessmentContext 
            INNER JOIN
            SourceFeed.dbo.JcccustomersAssessmentScoreProxy D WITH(NOLOCK)
            ON B.myJCJccUserFormContext = D.myJcAssessmentContext


            INNER JOIN
            TEMP_DATABASE.dbo.jsystemReport_toyClusterReviewEvent_AllPersons    A WITH(NOLOCK)
            ON a.myAssessment = B.oid
            INNER JOIN
            TEMP_DATABASE.dbo.jsystemAssessment             ass WITH(NOLOCK)
            ON a.getcustomersId = ass.customersID
            AND a.EVENT_SSID = ass.EVENTID
WHERE       NOT EXISTS (SELECT  OID
                        FROM    (SELECT     tp1.OID
                                    FROM    TEMP_DATABASE.dbo.AssessmentTransferPart1   tp1
                                    UNION ALL
                                    SELECT  tp2.OID
                                    FROM    TEMP_DATABASE.dbo.AssessmentTransferPart2   tp2) jcc
                        WHERE   jcc.OID = C.OID)

这里是link to the actual execution plan。


JccClientAssessmentProxy jcc 添加索引之后 WITH(NOLOCK,INDEX=IX_AssessmentProxy_GETMYCOORDCAREPLANVER) 这里是the new execution plan。

【问题讨论】:

您添加了哪个文本列?您刚刚在 SELECT 子句中添加了一列,它会减慢您的查询速度?你必须更具体,这样你的问题不是很容易理解。可能添加带有表结构等的 SQL Fiddle。 getQuestionId 是列 注意粗体等标记在格式化的代码块中不起作用。 你应该在这里发布一个执行计划,有a website that allows for posting complicated plans不需要发布图像。 您在JccClientAssesmentProxy 上进行了多次表扫描和一次 RID 查找,这需要 30.8% 的时间。您应该改进该表的索引。 SSMS Execution plan 给出了一些索引建议,请仔细查看并修改您的索引。 【参考方案1】:

有问题的部分是:

  ,CAST(a.getQuestionId AS VARCHAR(300)) AS QUESTION_ID  
  FROM  cnlPjccR_Report.dbo.JccClientAssessmentProxy AS a  
        WITH(NOLOCK,INDEX=IX_AssessmentProxy_getMyEventItem)
  INNER JOIN ...

并且问题在您的两个 UNION ALL 子查询中重复出现。

您的执行计划显示成本很高Key lookups

第二种方法是看看你是否可以创建一个“覆盖索引” 满足整个查询或至少消除关键查找。 A “覆盖索引”只是一个非聚集索引,它包含所有 满足整个查询或在我们的例子中需要的列, 消除对密钥查找操作的需要。挑战之一是获得 生成键查找的列列表。你可以这样做 在 SQL Server Management Studio (SSMS) 中通过右键单击键 查找运算符,然后选择属性。然后找到输出 在“属性”窗口中列出行,然后单击省略号按钮。 这将打开一个窗口(见下文),其中包含所有列的列表 键查找正在寻找。您可以使用此列表来帮助您 决定是否以及如何创建索引来“覆盖”查询或键 查找。

在您的情况下,应包含以下列以创建覆盖索引:

[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].answer; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].answerNumber; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].getMyCoordCarePlanVer; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].getQuestionId; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].myAssessment; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].myJcAssessmentContext; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].questionNumber; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].questionText; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].templateName; 
[cnlPjccR_Report].[dbo].[JccClientAssessmentProxy].templateVersion

您的索引可能不包含 getQuestionId 列,而这一事实在您的查询执行计划中造成了较慢的 Key lookupRID lookup

见also here 或this answer。

【讨论】:

以上是关于如何提高下面提到的 SQL 查询中的文本列性能的主要内容,如果未能解决你的问题,请参考以下文章

如何提高具有数组列的 DataFrame 的 Spark SQL 查询性能?

如何对Oracle sql 进行性能优化的调整

如何提高 SQL Azure 查询性能

如何提高Oracle中动态sql的查询性能

如何提高sql中的查询性能?

如何提高我的查询性能 SQL Server