【问题标题】:Get records that have all sub records获取包含所有子记录的记录
【发布时间】:2015-08-17 23:37:41
【问题描述】:

我有一个名为 Medicine 的表和一个名为 Medication_symptoms 的关联子表

医药

MediId, Name
1,      MedA
2,      MedB
3,      MedC

药物治疗症状

MedSympId, Medicine (MedId),     Symptom (symptomId)
1,         MedA (1),             Symptom A (1)
2,         MedA (1),             Symptom B (2)
3,         MedB (2),             Symptom B (2)
4,         MedB (2),             Symptom C (3)
5,         MedC (3),             Symptom D

我有另一个名为 Patient 和 Patient_Symptoms 的表

患者

PatientId, Name
1,         Patient A
2,         Patient B
3,         Patient C
4,         Patient D

Patient_Symptom

PatientSymptomId, PatientId,     SymptomId
1,                Patient A(1),  Symptom A (1)
2,                Patient A(1),  Symptom B (2)
3,                Patient B(2),  Symptom B (2)
4,                Patient B(2),  Symptom D (4)
5,                Patient D(4),  Symptom D (4)

鉴于上述信息,我需要获得与患者所有症状相匹配的药物:(我会逐个提取每个患者的信息)

Patient A - Med A (as he has symptom a and b and Med is for symptom A and B)
Patient B - None! (as he has symptoms b and d and there is no medicine for symptoms B and D)
Patient D - Med C (as med C is for symptom D only and Patient D has only symptom D)

注意症状是一个单独的表格:

症状

Symptom Id, Name
1,          Symptom A
2,          Symptom B
3,          Symptom C
4,          Symptom D
5,          Symptom E

这样的查询叫什么?

注意:我已经编造了这个例子。在我正在做的事情中,我有一个记录 A,带有一组属性(其中属性存储为记录 A 的记录行)。我需要将该记录 A 与另一条记录 C 匹配,该记录 C 具有与 A 完全相同的一组属性。(有意义吗?)

您可以使用http://pastebin.com/kaqdtHf3 处的脚本创建表格和一些示例数据

【问题讨论】:

  • 请在您的示例数据中包含列名。
  • 您介意解释一下您如何为患者确定药物的逻辑吗?例如,我看不到Patient B 是如何产生None! 的。
  • 匹配是否必须 100% 准确?例如,如果患者X 有症状AB。并且药物Y是针对症状ABC定义的,药物Y是否应该退回?
  • 这样的查询叫什么? 就关系代数而言,您要寻找的是除法(您将患者的症状集与药物治疗的症状以获得可以治疗所有症状的药物)。 Celko 有一篇关于这个主题的好文章,有一些不同的解决方案:Divided We Stand: The SQL of Relational Division
  • @jpw,关于关系划分的文章 - 太棒了!谢谢!

标签: sql sql-server


【解决方案1】:

您可以结合not existsfull join … null 来选择所有不-不-(双阴性)对患者的症状有治疗作用的药物-因此该药物确实具有所有治疗作用

select * from medicine m
where not exists (
    select 1 from patient_symptom ps 
    full join medication_symptoms ms on ps.SymptomId = ms.SymptomId
          and ps.PatientId = :myPatientIdHere
          and ms.MedId = m.MedId
    where (ms.SymptomId is null or ps.symptomId is null)
)

另一种使用条件聚合排除任何不能治疗患者症状的药物的方法

select ms.MedId
from patient_symptom ps
join medication_symptoms ms on ps.SymptomId = ms.SymptomId
where ps.patientId = :myPatientIdHere
group by ms.MedId, ps.patientId
having count(ms.symptomId) = (select count(*) from patient_symptom ps2 
                                where ps2.patientId = ps.patientId)
and count(ms.symptomId) = (select count(*) from medication_symptoms ms2 
                                where ms2.MedId = ms.MedId)

更新

如果您使用full join,您可以使用条件聚合来确保完全联接的任一侧都没有空值,从而确保存在 1:1 匹配。

select t1.MedId
from (
  select * from 
  patient_symptom ps
  cross join medicine m
  where patientId = :myPatientId
) t1
full join medication_symptoms ms on t1.SymptomId = ms.SymptomId
      and t1.MediId = ms.MediId
group by t1.MedId
having count(case when t1.SymptomId is null or ms.SymptomId is null then 1 end) = 0

【讨论】:

  • 当我有症状 A、B、C 的 Med A 和有症状 A 和 B 的患者 A 时,它仍然返回 Med A(不希望它返回)。完整的外部连接似乎没有帮助。此外,第二个查询没有处理以下错误 - HAVING 子句中的列 'patient_symptom.PatientId' 无效,因为它不包含在聚合函数或 GROUP BY 子句中。
  • 我不知道你是怎么做到这么快的! #尊重
  • 第一个查询仍然使我获得 Med A。但是第二个查询完全符合我的要求。这叫什么类型的查询? (这样我就可以阅读更多相关信息)
  • 当我将第一个查询更改为: select * from Medicine m where not exists ( select 1 from patient_symptom ps full join drug_symptom ms on ps.SymptomId = ms.SymptomId和 ps.PatientId = :myPatientIdHere 其中 ms.MedicineId = m.Id 和 (ms.SymptomId 为空或 ps.symptomId 为空))
  • 似乎问题与有关患者 ID 的条款的放置位置有关。它在与 ON 子句一起使用时起作用。如果它在哪里不起作用。在 ps.SymptomId = ms.SymptomId 和 ps.PatientId = :myPatientIdHere 上完全加入 drug_symptom ms
【解决方案2】:

FuzzyTree 发布了多个答案。这是他的第一个查询,其中包含我必须进行的所有更改才能使其正常工作。他的查询 #2 也有效。

SELECT *
FROM medicine m
WHERE NOT EXISTS (
        SELECT 1
        FROM (
            SELECT ms.symptomId
            FROM Medication_Symptoms ms
            WHERE ms.medId = m.medid
            ) ms1
        FULL JOIN (
            SELECT ps.SymptomId
            FROM Patient_Symptom ps
            WHERE ps.PatientId = 7
            ) ps1 ON ps1.SymptomId = ms1.SymptomId
        WHERE (
                ps1.SymptomId IS NULL
                OR ms1.symptomId IS NULL
                )
        )

我们发现下面的查询比上面的查询更快(这是由一位同事发现的,并查看了挂钟时间和查询计划,这个更快)

select m.Name
from Medicine m
where m.Id in (
    select ms.MedicineId
    from       Medication_Symptom ms
    inner join (select SymptomId
        from Patient_Symptom
        where PatientId = 7)      ps on ps.SymptomId = ms.SymptomId
    group by ms.MedicineId
    having count(*) = (select count(SymptomId)
        from Patient_Symptom
        where PatientId = 7)
    intersect
    select ms.MedicineId
    from Medication_Symptom ms
    group by ms.MedicineId
    having count(*) = (select count(SymptomId)
        from Patient_Symptom
        where PatientId = 7)
    )

最后,此查询返回所有患者的数据:

select po.Name, m.Name
from Medicine m, patient po
where m.Id in (
    select ms.MedicineId
    from       Medication_Symptom ms
    inner join (select SymptomId
        from Patient_Symptom
        where PatientId = po.Id)      ps on ps.SymptomId = ms.SymptomId
    group by ms.MedicineId
    having count(*) = (select count(SymptomId)
        from Patient_Symptom
        where PatientId = po.Id)
    intersect
    select ms.MedicineId
    from Medication_Symptom ms
    group by ms.MedicineId
    having count(*) = (select count(SymptomId)
        from Patient_Symptom
        where PatientId = po.Id)
    )

【讨论】:

    猜你喜欢
    • 2013-04-08
    • 2016-02-19
    • 1970-01-01
    • 2022-12-06
    • 1970-01-01
    • 2015-11-30
    • 1970-01-01
    • 1970-01-01
    • 2017-05-13
    相关资源
    最近更新 更多