1

我正在处理对表单进行审计的查询。有几页需要审核。当表单填写的答案被存储在两个表中的以下方式:极其缓慢的查询,其中包含多个子查询SQL Server 2008 R2

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_vst_summ QOC将答案存储在一行中,因此每次访问只有一行。表3是一样的,每个访问ID只有一行。

我的查询通过收集存储在表中的VISIT IDS开始,然后传递到下一组以回答一些问题。我从其他表中抽取访问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 ...[]...[]...[] 

你会注意到,我使用的是NOT IN条款,其原因是,如果问题没有问或回答,就没有记录,甚至没有NULL ,所以如果我不使用它,人们可以完成所有其他事情,但如果不是那个特定的项目,那么他们将被排除在最终结果集之外。

如果我需要澄清,请让我知道。

** QUERY实际执行计划XML ** query exec actual xml

谢谢

+0

请在某处上传实际(未估计)执行计划的XML。 –

+0

会这么做,它需要大约30分钟左右才能运行,以显示两个需要子查询的问题。 –

+0

在这种情况下,在尝试用'CREATE TABLE#T1(VISIT_ID VARCHAR(20)PRIMARY KEY)'替换表变量'@ T1'之前。这可能有帮助,但至少不应妨碍。 –

回答

4

smsmir.obsv一种观点认为UNION -s一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 
+0

我现在就试试这个,由于某种原因,我从来没有收到通知,当我从课程上课回家后,我发布了一个答案,现在开始运行并将发布结果。 –

+0

好吧,这工作真的很快(相对于我的,0:4:56:00),但是当一条记录没有结果时,我得到一个空值为VISIT ID而不是像123456789这样的实际ID。这是非常重要的我们想要调查为什么事情没有完成,特别是在存在0的情况下获得ID号。所以,如果我们能够解决这个问题,那么这将得到明显的答案,你会得到积分。这里是新的实际执行XML https://app.box.com/s/stlhmh8j5m8tmu7yopds –

+1

@MCP_infiltrator - 哦,是的。 SELECT SELECT#T1.VISIT_ID' –

0

你不必再次打表,你已经有了数据。汇总它。

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