如何提高下面提到的 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 lookup
或 RID lookup
变慢。
见also here or this answer。
有一个 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 lookup
或 RID lookup
变慢。
见also here or this answer。