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

How to improve the text column performance in below mentioned SQL query

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

JcccustomersAssessmentProxy 上创建了 3 个索引。这是一个有 8000 万条记录的 table。索引的显式使用仅在第三个查询中提到 (即(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.

有问题的部分是:

  ,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.

The second method is to see if you can create a “covering index” that satisfies the entire query or at least eliminates the key lookups. A “covering index” is simply a non-clustered index that has all of the columns needed to either satisfy the entire query or in our case, eliminate the need for a key lookup operation. One challenge is to get a list of columns that are generating the key lookup. You can do this in SQL Server Management Studio (SSMS) by right-clicking on the key lookup operator, and then choosing Properties. Then find the Output List row in the Properties window, and click on the ellipsis button. This will open a window (see below) with a list of all of the columns that the key lookup is looking for. You can use this list to help you decide whether and how to create an index to “cover” the query or key lookup.

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

[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 or this answer