从主查询中排除子查询结果
Exclude subquery results from main query
我的主查询 (COPD) 中有一组患者,子查询 (CANC) 中有另一组患者。我想从主要查询结果中排除 CANC PAT_ID,但这似乎不起作用并且运行时间太长。有没有更好的方法来排除子查询结果?我尝试了 NOT EXISTS 和 NOT IN,但我认为我做的不对,因为本应被排除在外的患者仍在出现。
SELECT DISTINCT
pe.PAT_ENC_CSN_ID,
pe.PAT_ID,
pe.CONTACT_DATE,
vp.PAT_MRN_ID,
vp.PAT_NAME,
vp.SEX_NAME,
pat.BIRTH_DATE,
vp.AGE_YEARS,
vp.CUR_PCP_NAME
FROM PAT_ENC pe
INNER JOIN V_PAT_FACT vp on pe.PAT_ID=vp.PAT_ID
INNER JOIN PATIENT pat on vp.PAT_ID=pat.PAT_ID
INNER JOIN CLARITY_ADT adt on pe.PAT_ENC_CSN_ID=adt.PAT_ENC_CSN_ID
LEFT OUTER JOIN PAT_ENC_DX dx on pe.PAT_ID=dx.PAT_ID
LEFT OUTER JOIN CLARITY_EDG edg on dx.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS gcr on edg.DX_ID=gcr.COMPILED_REC_LIST_VALUE
------- EXCLUSION CANCER
LEFT JOIN
(
SELECT DISTINCT pl.PAT_ID
FROM PROBLEM_LIST pl
LEFT OUTER JOIN CLARITY_EDG edg on pl.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS rec on edg.DX_ID=rec.COMPILED_REC_LIST_VALUE
WHERE rec.GROUPER_ID in ('2100000011')
)cx on pe.PAT_ID=cx.PAT_ID
WHERE pe.CONTACT_DATE > '2016-07-01 00:00:00.000'
AND pe.WEIGHT>= '1587.3' -- 45 kg or more
AND vp.AGE_YEARS BETWEEN '40' AND '80'
AND vp.SEX_C in ('1','2') --FEMALE or MALE
AND adt.PAT_CLASS_C in ('101','103','104') ---IP, OBS or ED
AND vp.IS_VALID_PAT_YN = 'Y' -- NOT TEST
AND pat.PAT_STATUS_C <>'2' --NOT DECEASED
AND cx.PAT_ID IS NULL
加盟条件
您的加入条件没有意义。具体来说:
FROM PROBLEM_LIST pl
LEFT OUTER JOIN CLARITY_EDG edg on pl.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS rec on edg.DX_ID=rec.COMPILED_REC_LIST_VALUE
在上面你的 LEFT JOIN
到 CLARITY_EDG
table 然后 INNER JOIN
到 GROUPER_COMPILED_RECORDS
。 INNER JOIN
要求左table和右table都有记录,因此将前面的LEFT JOIN
转化为INNER JOIN
.
假设您需要加入所有 table,您需要将 LEFT JOIN
更改为 INNER JOIN
。
主查询也需要修改。
更新(感谢@ThorstenKettner 指出了问题):我删除了我的示例查询,因为它没有意义。
查询性能
DISTINCT
- 对性能产生负面影响,因为 SQL 服务器实际上必须对结果集进行自连接以检查重复项。检查结果,看看你是否真的需要它。如果您确实得到重复项,请找到生成它们的 JOIN
并添加更多连接条件。
WHERE
- 指定常量时数据类型不匹配,例如
AND vp.AGE_YEARS BETWEEN '40' AND '80'
如果您的 AGE_YEARS
列是 INT
,请确保您的 BETWEEN
条件也指定了 INT
,例如BETWEEN 40 AND 80
。
如果数据类型不同,它会强制 SQL 服务器进行类型转换,在上述情况下,它会将整个 table 列转换为字符串(而不是将常量转换为 int)以评估条件.对于大的 tables 它不会很快,它也会阻止 SQL 服务器在此列上使用索引(如果有创建的话)。
因为我没有要检查的数据,所以我不再假设你的左连接(排除连接)是正确的,尽管有一个左连接然后是一个内部连接,但我把它改成了左连接。
SELECT DISTINCT
pe.PAT_ENC_CSN_ID,
pe.PAT_ID,
pe.CONTACT_DATE,
vp.PAT_MRN_ID,
vp.PAT_NAME,
vp.SEX_NAME,
pat.BIRTH_DATE,
vp.AGE_YEARS,
vp.CUR_PCP_NAME
FROM PAT_ENC pe
INNER JOIN V_PAT_FACT vp on pe.PAT_ID=vp.PAT_ID
INNER JOIN PATIENT pat on vp.PAT_ID=pat.PAT_ID
INNER JOIN CLARITY_ADT adt on pe.PAT_ENC_CSN_ID=adt.PAT_ENC_CSN_ID
LEFT OUTER JOIN PAT_ENC_DX dx on pe.PAT_ID=dx.PAT_ID
LEFT OUTER JOIN CLARITY_EDG edg on dx.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS gcr on edg.DX_ID=gcr.COMPILED_REC_LIST_VALUE
WHERE pe.CONTACT_DATE > '2016-07-01 00:00:00.000'
AND pe.WEIGHT>= '1587.3' -- 45 kg or more
AND vp.AGE_YEARS BETWEEN '40' AND '80'
AND vp.SEX_C in ('1','2') --FEMALE or MALE
AND adt.PAT_CLASS_C in ('101','103','104') ---IP, OBS or ED
AND vp.IS_VALID_PAT_YN = 'Y' -- NOT TEST
AND pat.PAT_STATUS_C <>'2' --NOT DECEASED
------- EXCLUSION CANCER
AND pe.PAT_ID not in
(
SELECT pl.PAT_ID
FROM PROBLEM_LIST pl
LEFT OUTER JOIN CLARITY_EDG edg on pl.DX_ID=edg.DX_ID
LEFT JOIN GROUPER_COMPILED_RECORDS rec on edg.DX_ID=rec.COMPILED_REC_LIST_VALUE
AND rec.GROUPER_ID in ('2100000011')
)
首先:DISTINCT
通常表示查询编写不当。在编写良好的查询中,很少需要从结果中删除重复项,您首先要避免产生重复项。在您的主查询中,您 select 来自表 PAT_ENC
、V_PAT_FACT
和 PATIENT
的数据。但是,您还连接了其他四个表,因此可能会出现重复的行。 也许您将这些表作为限制行的手段,即您只需要在这些表中具有匹配项的行。但是,你为什么要尝试加入他们呢?外部联接不代表限制。 (此外,无论如何,您的外部连接通过内部连接 grouper_compiled_records
变成内部连接。)
至于排除部分:由于与上述相同的原因,您再次进行了失败的外部连接。您正在使用总是有点难以阅读的反连接。我不知道为什么 NOT EXISTS
和 NOT IN
对你失败了。它们优于反连接,因为它们更易于阅读和理解。
您的查询应如下所示:
SELECT
pe.pat_enc_csn_id,
pe.pat_id,
pe.contact_date,
vp.pat_mrn_id,
vp.pat_name,
vp.sex_name,
pat.birth_date,
vp.age_years,
vp.cur_pcp_name
FROM pat_enc pe
JOIN v_pat_fact vp ON pe.pat_id = vp.pat_id
JOIN patient pat ON vp.pat_id = pat.pat_id
WHERE pe.pat_enc_csn_id IN
(
SELECT pat_enc_csn_id
FROM clarity_adt
WHERE pat_class_c IN (101, 103, 104) ---IP, OBS or ED
)
AND pe.pat_id IN
(
SELECT dx.pat_id
FROM pat_enc_dx dx
JOIN clarity_edg edg on dx.dx_id = edg.dx_id
JOIN grouper_compiled_records gcr on edg.dx_id = gcr.compiled_rec_list_value
)
AND pe.contact_date > '2016-07-01'
AND pe.weight >= 1587.3 -- 45 kg or more
AND vp.age_years BETWEEN 40 AND 80
AND vp.sex_c IN (1, 2) -- female or male
AND vp.is_valid_pat_yn = 'Y' -- not test
AND pat.pat_status_c <> 2 --not deceased
AND pe.pat_id NOT IN -- exclude cancer patients
(
SELECT pl.pat_id
FROM problem_list pl
JOIN clarity_edg edg ON pl.dx_id = edg.dx_id
JOIN grouper_compiled_records rec ON edg.dx_id = rec.compiled_rec_list_value
WHERE rec.grouper_id = 2100000011
);
(假设 problem_list.pat_id
可以为 null,因为列表中的 null 会使 NOT IN
失败。您必须添加 AND pl.pat_id IS NOT NULL
在列可以为空的不太可能的情况下添加到您的子查询。)
不过,数据模型看起来有点奇怪。好像一个patient
,由pat_id
标识,可以有几个pat_enc
和几个v_pat_fact
。但是,为什么要为每个患者创建所有组合呢?或者是否存在 1:1 关系,也许每个 patient
正好是一个 v_pat_fact
?但是为什么要分开表呢?当你知道生日时,为什么要存储一个年龄(每年都会变化)?
我不能确定这正是您要查找的查询,但应该很接近,您应该能够根据需要进行调整。
我的主查询 (COPD) 中有一组患者,子查询 (CANC) 中有另一组患者。我想从主要查询结果中排除 CANC PAT_ID,但这似乎不起作用并且运行时间太长。有没有更好的方法来排除子查询结果?我尝试了 NOT EXISTS 和 NOT IN,但我认为我做的不对,因为本应被排除在外的患者仍在出现。
SELECT DISTINCT
pe.PAT_ENC_CSN_ID,
pe.PAT_ID,
pe.CONTACT_DATE,
vp.PAT_MRN_ID,
vp.PAT_NAME,
vp.SEX_NAME,
pat.BIRTH_DATE,
vp.AGE_YEARS,
vp.CUR_PCP_NAME
FROM PAT_ENC pe
INNER JOIN V_PAT_FACT vp on pe.PAT_ID=vp.PAT_ID
INNER JOIN PATIENT pat on vp.PAT_ID=pat.PAT_ID
INNER JOIN CLARITY_ADT adt on pe.PAT_ENC_CSN_ID=adt.PAT_ENC_CSN_ID
LEFT OUTER JOIN PAT_ENC_DX dx on pe.PAT_ID=dx.PAT_ID
LEFT OUTER JOIN CLARITY_EDG edg on dx.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS gcr on edg.DX_ID=gcr.COMPILED_REC_LIST_VALUE
------- EXCLUSION CANCER
LEFT JOIN
(
SELECT DISTINCT pl.PAT_ID
FROM PROBLEM_LIST pl
LEFT OUTER JOIN CLARITY_EDG edg on pl.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS rec on edg.DX_ID=rec.COMPILED_REC_LIST_VALUE
WHERE rec.GROUPER_ID in ('2100000011')
)cx on pe.PAT_ID=cx.PAT_ID
WHERE pe.CONTACT_DATE > '2016-07-01 00:00:00.000'
AND pe.WEIGHT>= '1587.3' -- 45 kg or more
AND vp.AGE_YEARS BETWEEN '40' AND '80'
AND vp.SEX_C in ('1','2') --FEMALE or MALE
AND adt.PAT_CLASS_C in ('101','103','104') ---IP, OBS or ED
AND vp.IS_VALID_PAT_YN = 'Y' -- NOT TEST
AND pat.PAT_STATUS_C <>'2' --NOT DECEASED
AND cx.PAT_ID IS NULL
加盟条件
您的加入条件没有意义。具体来说:
FROM PROBLEM_LIST pl
LEFT OUTER JOIN CLARITY_EDG edg on pl.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS rec on edg.DX_ID=rec.COMPILED_REC_LIST_VALUE
在上面你的 LEFT JOIN
到 CLARITY_EDG
table 然后 INNER JOIN
到 GROUPER_COMPILED_RECORDS
。 INNER JOIN
要求左table和右table都有记录,因此将前面的LEFT JOIN
转化为INNER JOIN
.
假设您需要加入所有 table,您需要将 LEFT JOIN
更改为 INNER JOIN
。
主查询也需要修改。
更新(感谢@ThorstenKettner 指出了问题):我删除了我的示例查询,因为它没有意义。
查询性能
DISTINCT
- 对性能产生负面影响,因为 SQL 服务器实际上必须对结果集进行自连接以检查重复项。检查结果,看看你是否真的需要它。如果您确实得到重复项,请找到生成它们的 JOIN
并添加更多连接条件。
WHERE
- 指定常量时数据类型不匹配,例如
AND vp.AGE_YEARS BETWEEN '40' AND '80'
如果您的 AGE_YEARS
列是 INT
,请确保您的 BETWEEN
条件也指定了 INT
,例如BETWEEN 40 AND 80
。
如果数据类型不同,它会强制 SQL 服务器进行类型转换,在上述情况下,它会将整个 table 列转换为字符串(而不是将常量转换为 int)以评估条件.对于大的 tables 它不会很快,它也会阻止 SQL 服务器在此列上使用索引(如果有创建的话)。
因为我没有要检查的数据,所以我不再假设你的左连接(排除连接)是正确的,尽管有一个左连接然后是一个内部连接,但我把它改成了左连接。
SELECT DISTINCT
pe.PAT_ENC_CSN_ID,
pe.PAT_ID,
pe.CONTACT_DATE,
vp.PAT_MRN_ID,
vp.PAT_NAME,
vp.SEX_NAME,
pat.BIRTH_DATE,
vp.AGE_YEARS,
vp.CUR_PCP_NAME
FROM PAT_ENC pe
INNER JOIN V_PAT_FACT vp on pe.PAT_ID=vp.PAT_ID
INNER JOIN PATIENT pat on vp.PAT_ID=pat.PAT_ID
INNER JOIN CLARITY_ADT adt on pe.PAT_ENC_CSN_ID=adt.PAT_ENC_CSN_ID
LEFT OUTER JOIN PAT_ENC_DX dx on pe.PAT_ID=dx.PAT_ID
LEFT OUTER JOIN CLARITY_EDG edg on dx.DX_ID=edg.DX_ID
INNER JOIN GROUPER_COMPILED_RECORDS gcr on edg.DX_ID=gcr.COMPILED_REC_LIST_VALUE
WHERE pe.CONTACT_DATE > '2016-07-01 00:00:00.000'
AND pe.WEIGHT>= '1587.3' -- 45 kg or more
AND vp.AGE_YEARS BETWEEN '40' AND '80'
AND vp.SEX_C in ('1','2') --FEMALE or MALE
AND adt.PAT_CLASS_C in ('101','103','104') ---IP, OBS or ED
AND vp.IS_VALID_PAT_YN = 'Y' -- NOT TEST
AND pat.PAT_STATUS_C <>'2' --NOT DECEASED
------- EXCLUSION CANCER
AND pe.PAT_ID not in
(
SELECT pl.PAT_ID
FROM PROBLEM_LIST pl
LEFT OUTER JOIN CLARITY_EDG edg on pl.DX_ID=edg.DX_ID
LEFT JOIN GROUPER_COMPILED_RECORDS rec on edg.DX_ID=rec.COMPILED_REC_LIST_VALUE
AND rec.GROUPER_ID in ('2100000011')
)
首先:DISTINCT
通常表示查询编写不当。在编写良好的查询中,很少需要从结果中删除重复项,您首先要避免产生重复项。在您的主查询中,您 select 来自表 PAT_ENC
、V_PAT_FACT
和 PATIENT
的数据。但是,您还连接了其他四个表,因此可能会出现重复的行。 也许您将这些表作为限制行的手段,即您只需要在这些表中具有匹配项的行。但是,你为什么要尝试加入他们呢?外部联接不代表限制。 (此外,无论如何,您的外部连接通过内部连接 grouper_compiled_records
变成内部连接。)
至于排除部分:由于与上述相同的原因,您再次进行了失败的外部连接。您正在使用总是有点难以阅读的反连接。我不知道为什么 NOT EXISTS
和 NOT IN
对你失败了。它们优于反连接,因为它们更易于阅读和理解。
您的查询应如下所示:
SELECT
pe.pat_enc_csn_id,
pe.pat_id,
pe.contact_date,
vp.pat_mrn_id,
vp.pat_name,
vp.sex_name,
pat.birth_date,
vp.age_years,
vp.cur_pcp_name
FROM pat_enc pe
JOIN v_pat_fact vp ON pe.pat_id = vp.pat_id
JOIN patient pat ON vp.pat_id = pat.pat_id
WHERE pe.pat_enc_csn_id IN
(
SELECT pat_enc_csn_id
FROM clarity_adt
WHERE pat_class_c IN (101, 103, 104) ---IP, OBS or ED
)
AND pe.pat_id IN
(
SELECT dx.pat_id
FROM pat_enc_dx dx
JOIN clarity_edg edg on dx.dx_id = edg.dx_id
JOIN grouper_compiled_records gcr on edg.dx_id = gcr.compiled_rec_list_value
)
AND pe.contact_date > '2016-07-01'
AND pe.weight >= 1587.3 -- 45 kg or more
AND vp.age_years BETWEEN 40 AND 80
AND vp.sex_c IN (1, 2) -- female or male
AND vp.is_valid_pat_yn = 'Y' -- not test
AND pat.pat_status_c <> 2 --not deceased
AND pe.pat_id NOT IN -- exclude cancer patients
(
SELECT pl.pat_id
FROM problem_list pl
JOIN clarity_edg edg ON pl.dx_id = edg.dx_id
JOIN grouper_compiled_records rec ON edg.dx_id = rec.compiled_rec_list_value
WHERE rec.grouper_id = 2100000011
);
(假设 problem_list.pat_id
可以为 null,因为列表中的 null 会使 NOT IN
失败。您必须添加 AND pl.pat_id IS NOT NULL
在列可以为空的不太可能的情况下添加到您的子查询。)
不过,数据模型看起来有点奇怪。好像一个patient
,由pat_id
标识,可以有几个pat_enc
和几个v_pat_fact
。但是,为什么要为每个患者创建所有组合呢?或者是否存在 1:1 关系,也许每个 patient
正好是一个 v_pat_fact
?但是为什么要分开表呢?当你知道生日时,为什么要存储一个年龄(每年都会变化)?
我不能确定这正是您要查找的查询,但应该很接近,您应该能够根据需要进行调整。