非常慢的查询,包含多个子查询SQL Server 2008 R2
我正在处理一个对表单进行审计的查询。有好几页值得审核的问题。填写表格时,答案以以下方式存储在两个表中:非常慢的查询,包含多个子查询SQL Server 2008 R2,sql,optimization,sql-server-2008-r2,subquery,query-optimization,Sql,Optimization,Sql Server 2008 R2,Subquery,Query Optimization,我正在处理一个对表单进行审计的查询。有好几页值得审核的问题。填写表格时,答案以以下方式存储在两个表中: Table 1: smsmir.obsv OBS EPISODE NO | FORM USAGE | QUEST | ANSWER | ... 123456789 | ADMISSION | QUESTION 1 | YES | ... 123456789 | ADMISSION | QUESTION 2 | 150 | ... ... Table 2: sms
Table 1: smsmir.obsv OBS
EPISODE NO | FORM USAGE | QUEST | ANSWER | ...
123456789 | ADMISSION | QUESTION 1 | YES | ...
123456789 | ADMISSION | QUESTION 2 | 150 | ...
...
Table 2: smsdss.QOC_vst_summ QOC
EPISODE NO | HT IND | WT IND | ADV DIR | ...
123456789 | 1 | 1 | 0 | ...
...
表1:smsmir.obsv OBS
将信息存储在向量中,因此每个问题都有另一行。表2:smsdss.QOC\u vst\u sum QOC
将答案存储在一行中,因此每次访问只有一行。表3相同,每个访问id只有一行
我的查询首先收集存储在表中的访问ID
,然后传递到下一组以回答一些问题。我从另一个表中提取访问ID的原因是,访问开始和结束日期存储在该表中。那张桌子看起来像这样:
Table 3: smsdss.BMH_PLM_PtAcct_V PAV
EPISODE NO | ADM DATE | ...
123456789 | 2013-08-01 | ...
...
我得到的期望输出如下:
EPISODE NO | QUESTION 1 | QUESTION 2 | HT IND | WT IND | ADV DIR | ...
123456789 | 1 | 1 | 1 | 1 | 0 | ...
...
在上表中,1表示使用案例陈述回答了问题,0表示未回答。该查询已被重新编写,现在正在生成正确的结果,但速度非常慢,取回40条记录需要53分36秒。由于查询目前尚未完成,只有7列返回,因此我必须将其扩展为总共65列
我有子查询的原因是答案存储在一个向量中,每一行都是一个问题和答案,但是因为我想在列中显示答案和问题,所以我做了一个子查询。有没有更好的方法来加快速度
以下是查询:
-- THIS QUERY WILL PERFORM AN AUDIT OF THE ADMISSION ASSESSMENT AND
-- OTHER REQUIRED QUESTIONS BY NURSING INFORMATICS
-----------------------------------------------------------------------
-- VARIABLE DECLARATION AND INITIALIZATION. BY DECLARING A START AND
-- END DATE A USER CAN SIMPLY CHANGE THOSE PARAMETERS AND AUDIT ALL
-- INPATIENT ADMISSION ASSESSMENTS FOR THAT TIME PERIOD
DECLARE @SD DATETIME
DECLARE @ED DATETIME
SET @SD = '2013-08-01'
SET @ED = '2013-08-01'
-- QUERY 1
-- THIS QUERY CREATES A TABLE THAT WILL HOUSE ALL VISIT ID NUMBERS THAT
-- ARE GOING TO BE INCLUDED INSIDE OF THE ADMISSION ASSESSMENT AUDIT
-- TABLE DECLARATION ##################################################
DECLARE @T1 TABLE (
VISIT_ID VARCHAR(20))
-- ####################################################################
-- THESE ARE THE ITEMS THAT ARE GOING TO BE INSERTED INTO THE TABLE
INSERT INTO @T1
-- COLUMN SELECTION
SELECT A.PtNo_Num
-- DB(S) USED
FROM (SELECT DISTINCT PTNO_NUM
FROM smsdss.BMH_PLM_PtAcct_V
WHERE Adm_Date BETWEEN @SD AND @ED
AND Plm_Pt_Acct_Type = 'I') A
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
-----------------------------------------------------------------------
-- QUERY TWO. THIS QUERY WILL TAKE THE VISIT ID'S FROM QUERY 1 AND RUN
-- THEM THROUGH A SET OF RULES TO DECIDE WHEATHER OR NOT THE ADDMISISON
-- ASSESSMENT WAS PROPERLY DONE
-----------------------------------------------------------------------
-- COLUMN SELECTION
SELECT DISTINCT OBS.episode_no AS [VISIT ID]
-- CASE STATEMENT, IF PREFERRED LANGUAGE IS NOT 'NULL' THEN CONSIDER
-- THIS COMPLETE AND SCORE 1 ELSE CONSIDER INCOMPLETE AND SCORE 0
,
CASE
WHEN QOC.prim_lng IS NOT NULL THEN 1
ELSE 0
END AS [PREF LANG COMPLETE?],
QOC.ht_chtd_ind AS [HT IND],
QOC.wt_chtd_ind AS [WT IND],
QOC.adv_dir_ind AS [ADV DIRECTIVE]
-- A SEPERATE SELECT STATEMENT IS USED HERE BECAUSE RESULTS OF THE
-- ADMISSION CONSENT ARE STORED IN A VECTOR, SO IT IS NECESSARY TO
-- MAKE A SELECTION FROM THAT LIST, HERE A VALUE OF 1 = YES AND
-- 0 = NO
,
CASE
WHEN OBS.episode_no NOT IN (SELECT episode_no
FROM smsmir.obsv
WHERE form_usage = 'Admission') THEN 0
ELSE 1
END AS [ADMIT ASSESSMENT DONE],
CASE
WHEN OBS.episode_no NOT IN (SELECT episode_no
FROM smsmir.obsv
WHERE form_usage = 'Admission'
AND obsv_cd_ext_name = 'Admission consent signed:') THEN 0
ELSE 1
END AS [ADMIT CONSENT SIGNED?]
-- DB(S) USED ---------------------------------------------------------
FROM smsmir.obsv OBS
JOIN smsdss.QOC_vst_summ QOC
ON OBS.episode_no = QOC.episode_no
JOIN @T1 T1
ON OBS.episode_no = T1.VISIT_ID
-- FILTERS ------------------------------------------------------------
WHERE T1.VISIT_ID = OBS.episode_no
GROUP BY OBS.episode_no,
QOC.prim_lng,
QOC.ht_chtd_ind,
QOC.wt_chtd_ind,
QOC.adv_dir_ind,
OBS.obsv_cd_ext_name
--#####################################################################
-- END REPORT ...[]...[]...[]
您会注意到,我使用的是不在
子句中,原因是如果一个问题没有被问到或回答,就不会有记录,甚至不会有一个NULL
,所以如果我不使用它,这个人可以完成所有其他事情,但如果不是那个特定的项目,那么他们就被排除在最终结果集中
如果我需要澄清,请让我知道
**查询实际执行计划XML**
谢谢
smsmir。obsv
是一个视图,它包含一个155569000行的表和一个15375000行的表
执行计划显示这些表被扫描了42次
这绝大多数是因为表变量的默认基数估计值很低,这意味着嵌套循环的选择不恰当。用#temp
表替换应该可以解决这个问题
另外,使用PIVOT
技术而不是单独的子查询可以进一步减少这种情况。在添加缺少的索引方面,可能会有额外的优化,但您能否尝试一下,并让我知道时间安排和执行计划
DECLARE @SD DATETIME = '2013-08-01';
DECLARE @ED DATETIME = '2013-08-01';
CREATE TABLE #T1
(
VISIT_ID VARCHAR(20) UNIQUE CLUSTERED
)
INSERT INTO #T1
SELECT DISTINCT PTNO_NUM
FROM smsdss.BMH_PLM_PtAcct_V
WHERE Adm_Date BETWEEN @SD AND @ED
AND Plm_Pt_Acct_Type = 'I'
OPTION (RECOMPILE);
WITH OBS
AS (SELECT episode_no,
MAX(CASE
WHEN form_usage = 'Admission' THEN 1
END) AS [ADMIT ASSESSMENT DONE],
MAX(CASE
WHEN form_usage = 'Admission'
AND obsv_cd_ext_name = 'Admission consent signed:' THEN 1
END) AS [ADMIT CONSENT SIGNED?]
FROM smsmir.obsv
WHERE form_usage = 'Admission'
GROUP BY episode_no)
SELECT OBS.episode_no AS [VISIT ID],
CASE
WHEN QOC.prim_lng IS NOT NULL THEN 1
ELSE 0
END AS [PREF LANG COMPLETE?],
QOC.ht_chtd_ind AS [HT IND],
QOC.wt_chtd_ind AS [WT IND],
QOC.adv_dir_ind AS [ADV DIRECTIVE],
ISNULL(OBS.[ADMIT ASSESSMENT DONE], 0) AS [ADMIT ASSESSMENT DONE],
ISNULL(OBS.[ADMIT CONSENT SIGNED?], 0) AS [ADMIT CONSENT SIGNED?]
FROM smsdss.QOC_vst_summ QOC
JOIN #T1
ON #T1.VISIT_ID = QOC.episode_no
LEFT JOIN OBS
ON OBS.episode_no = QOC.episode_no
DROP TABLE #T1
smsmir.obsv
是一个视图,它包含一个155569000行的表和一个15375000行的表
执行计划显示这些表被扫描了42次
这绝大多数是因为表变量的默认基数估计值很低,这意味着嵌套循环的选择不恰当。用#temp
表替换应该可以解决这个问题
另外,使用PIVOT
技术而不是单独的子查询可以进一步减少这种情况。在添加缺少的索引方面,可能会有额外的优化,但您能否尝试一下,并让我知道时间安排和执行计划
DECLARE @SD DATETIME = '2013-08-01';
DECLARE @ED DATETIME = '2013-08-01';
CREATE TABLE #T1
(
VISIT_ID VARCHAR(20) UNIQUE CLUSTERED
)
INSERT INTO #T1
SELECT DISTINCT PTNO_NUM
FROM smsdss.BMH_PLM_PtAcct_V
WHERE Adm_Date BETWEEN @SD AND @ED
AND Plm_Pt_Acct_Type = 'I'
OPTION (RECOMPILE);
WITH OBS
AS (SELECT episode_no,
MAX(CASE
WHEN form_usage = 'Admission' THEN 1
END) AS [ADMIT ASSESSMENT DONE],
MAX(CASE
WHEN form_usage = 'Admission'
AND obsv_cd_ext_name = 'Admission consent signed:' THEN 1
END) AS [ADMIT CONSENT SIGNED?]
FROM smsmir.obsv
WHERE form_usage = 'Admission'
GROUP BY episode_no)
SELECT OBS.episode_no AS [VISIT ID],
CASE
WHEN QOC.prim_lng IS NOT NULL THEN 1
ELSE 0
END AS [PREF LANG COMPLETE?],
QOC.ht_chtd_ind AS [HT IND],
QOC.wt_chtd_ind AS [WT IND],
QOC.adv_dir_ind AS [ADV DIRECTIVE],
ISNULL(OBS.[ADMIT ASSESSMENT DONE], 0) AS [ADMIT ASSESSMENT DONE],
ISNULL(OBS.[ADMIT CONSENT SIGNED?], 0) AS [ADMIT CONSENT SIGNED?]
FROM smsdss.QOC_vst_summ QOC
JOIN #T1
ON #T1.VISIT_ID = QOC.episode_no
LEFT JOIN OBS
ON OBS.episode_no = QOC.episode_no
DROP TABLE #T1
你不必再敲桌子了,你已经有数据了。把它加起来
DECLARE @SD DATETIME
DECLARE @ED DATETIME
SET @SD = '2013-08-01'
SET @ED = '2013-08-01'
DECLARE @T1 TABLE (
VISIT_ID VARCHAR(20))
INSERT INTO @T1
SELECT A.PtNo_Num
FROM (SELECT DISTINCT PTNO_NUM
FROM smsdss.BMH_PLM_PtAcct_V
WHERE Adm_Date BETWEEN @SD AND @ED
AND Plm_Pt_Acct_Type = 'I') A
SELECT DISTINCT OBS.episode_no AS [VISIT ID],
CASE
WHEN QOC.prim_lng IS NOT NULL THEN 1
ELSE 0
END AS [PREF LANG COMPLETE?],
QOC.ht_chtd_ind AS [HT IND],
QOC.wt_chtd_ind AS [WT IND],
QOC.adv_dir_ind AS [ADV DIRECTIVE],
max(CASE
WHEN form_usage = 'Admission' THEN 1
ELSE 0
END) AS [ADMIT ASSESSMENT DONE],
max(CASE
WHEN form_usage = 'Admission' AND obsv_cd_ext_name = 'Admission consent signed:' THEN 1
ELSE 0
END ) AS [ADMIT CONSENT SIGNED?]
FROM smsmir.obsv OBS
JOIN smsdss.QOC_vst_summ QOC
ON OBS.episode_no = QOC.episode_no
JOIN @T1 T1
ON OBS.episode_no = T1.VISIT_ID
WHERE T1.VISIT_ID = OBS.episode_no
GROUP BY OBS.episode_no,
QOC.prim_lng,
QOC.ht_chtd_ind,
QOC.wt_chtd_ind,
QOC.adv_dir_ind,
OBS.obsv_cd_ext_name
你不必再敲桌子了,你已经有数据了。把它加起来
DECLARE @SD DATETIME
DECLARE @ED DATETIME
SET @SD = '2013-08-01'
SET @ED = '2013-08-01'
DECLARE @T1 TABLE (
VISIT_ID VARCHAR(20))
INSERT INTO @T1
SELECT A.PtNo_Num
FROM (SELECT DISTINCT PTNO_NUM
FROM smsdss.BMH_PLM_PtAcct_V
WHERE Adm_Date BETWEEN @SD AND @ED
AND Plm_Pt_Acct_Type = 'I') A
SELECT DISTINCT OBS.episode_no AS [VISIT ID],
CASE
WHEN QOC.prim_lng IS NOT NULL THEN 1
ELSE 0
END AS [PREF LANG COMPLETE?],
QOC.ht_chtd_ind AS [HT IND],
QOC.wt_chtd_ind AS [WT IND],
QOC.adv_dir_ind AS [ADV DIRECTIVE],
max(CASE
WHEN form_usage = 'Admission' THEN 1
ELSE 0
END) AS [ADMIT ASSESSMENT DONE],
max(CASE
WHEN form_usage = 'Admission' AND obsv_cd_ext_name = 'Admission consent signed:' THEN 1
ELSE 0
END ) AS [ADMIT CONSENT SIGNED?]
FROM smsmir.obsv OBS
JOIN smsdss.QOC_vst_summ QOC
ON OBS.episode_no = QOC.episode_no
JOIN @T1 T1
ON OBS.episode_no = T1.VISIT_ID
WHERE T1.VISIT_ID = OBS.episode_no
GROUP BY OBS.episode_no,
QOC.prim_lng,
QOC.ht_chtd_ind,
QOC.wt_chtd_ind,
QOC.adv_dir_ind,
OBS.obsv_cd_ext_name
请将实际(非估计)执行计划的XML上传到某个地方。如果可以的话,运行它大约需要30分钟左右的时间,以便显示需要子查询的两个问题。在这种情况下,请尝试用
创建表#T1替换表变量@T1
(访问ID VARCHAR(20)主键)
。这可能会有帮助,但至少不应该妨碍。@MartinSmith现在在一个单独的查询窗口中尝试,您还提到答案存储在两个表中,但随后向我们显示了三个,表和列的名称与查询中使用的名称不符。目前还不清楚预期结果如何与示例数据相对应。如果您提供更广泛的示例数据和结果,您可能会得到更好的答案。请将实际(非估计)执行计划的XML上载到某处。可以,为了显示需要子查询的两个问题,运行它大约需要30分钟。在这种情况下,请尝试将表变量@T1
替换为CREATE table#T1(VISIT_ID VARCHAR(20)主键)
。这可能会有帮助,但至少不应该妨碍。@MartinSmith现在在一个单独的查询窗口中尝试,您还提到答案存储在两个表中,但随后向我们显示了三个,表和列的名称与查询中使用的名称不符。目前还不清楚预期结果如何与示例数据相对应。如果你提供更广泛的示例数据和结果,你可能会得到一个更好的答案。我现在就试试,因为某种原因,我昨晚放学回家时从未被通知你发布了答案,现在就开始运行并发布结果。好吧,这非常快(相对于我所拥有的,0:4:56:00),但是当一条记录没有结果时,我会得到一个访问ID的空值,而不是像123456789这样的实际ID。获取ID号是非常重要的,尤其是在存在0的情况下,因为我们想调查为什么没有执行这些操作。如果我们能解决这个问题,那么这个问题就会被标记为“已回答”,你就会得到分数。这是新的实际执行XML@MCP_infirator-哦,是的。您需要的不是SELECT OBS.eposion\u no
而是SELECT\T1.VISIT\u ID
@MCP\u infirator-您的完整查询是否只对表格用法为“准入”的行感兴趣