【问题标题】:Searching for parent records whose children meet predicate搜索其子项满足谓词的父记录
【发布时间】:2013-03-07 16:13:25
【问题描述】:

假设我有一个父子数据库,而子数据库保存着父子发生的事情的一种运行记录:

create table patient (
   fullname text not null,
   admission_number integer primary key
);

create table history (
   note text not null,
   doctor text not null,
   admission_number integer references patient (admission_number)
);

(只是一个例子,我不是在做医疗应用)。

history 将有许多相同admission_number 的记录:

admission_number      doctor    note
------------------------------------
3456                  Johnson   Took blood pressure
7828                  Johnson   EKG 120, temp 99.2
3456                  Nichols   Drew blood
9001                  Damien    Discharged patient
7828                  Damien    Discharged patient with Rx

所以,我的问题是,我将如何构建一个查询来让我在注释字段中搜索和/或不搜索 patient 记录,例如,如果我想查找其历史记录包含的每位患者“血压”和“出院”。

现在我一直在 history 上进行 select 分组 admission_number,将所有注释与 group_concat(note) 结合起来并在 having 中进行搜索,因此:

select * from history 
  group by admission_number
  having group_concat(note) like '%blood pressure%' 
     and group_concat(note) like '%discharged';

这行得通,但它使某些详细说明变得非常复杂 - 例如,我希望能够询问“每位患者的病史包含‘血压’并且其与 Damien 医生的病史为‘出院, "并且在我的基本查询之上建立这样的资格非常混乱。

有没有更好的方式来表达我的基本查询?

【问题讨论】:

    标签: sql sqlite


    【解决方案1】:

    这类似于您的 EXISTS 方法,但计算子查询的方式不同。 这可能会也可能不会更快,具体取决于您的表和索引的组织方式以及查询的选择性。

    SELECT *
    FROM patient
    WHERE admission_number IN (SELECT admission_number
                               FROM history
                               WHERE note LIKE '%blood pressure%')
      AND admission_number IN (SELECT admission_number
                               FROM history
                               WHERE note LIKE '%discharged%'
                                 AND doctor = 'Damien')
    

    或者,您可以使用compound subquery(计算一次交集可能比对每条记录执行两次IN 更快):

    SELECT *
    FROM patient
    WHERE admission_number IN (SELECT admission_number
                               FROM history
                               WHERE note LIKE '%blood pressure%'
                               INTERSECT
                               SELECT admission_number
                               FROM history
                               WHERE note LIKE '%discharged%'
                                 AND doctor = 'Damien')
    

    【讨论】:

    • 我喜欢第二个,虽然使用复合操作的一个问题是你不能用括号将UNION/INTERSECTION/EXCEPT分组,它们必须从左到右分组-- 如果你在WHERE 子句中保留你的AND/OR/NOT 逻辑,那么你可以求助于括号来控制应用顺序。出于这个原因,我避免使用它们,但这是一个很好的解决方案。我会定义两个人的个人资料。
    • 其实我也想试试你的第一个;从查询生成代码的角度来看,它实际上可能是最优雅的——它允许您将原始比较谓词转换为完全格式的 SELECT 语句,而无需了解有关 FROM 表的任何信息。然后生成器可以将它们与正确的 AND/OR/NOT 和括号粘合在一起。
    • 您可以使用括号加上额外的SELECT,即SELECT * FROM ... EXCEPT SELECT * FROM (SELECT * FROM ... UNION SELECT * FROM ...)
    【解决方案2】:

    为什么不使用JOIN 操作?

    例如 考虑到,患者表包含以下数据:

    INSERT INTO patient VALUES('Bob', 3456);
    INSERT INTO patient VALUES('Mary', 7828);
    INSERT INTO patient VALUES('Lucy', 9001);
    

    运行查询:

    SELECT DISTINCT p.fullname, p.admission_number FROM patient p
    INNER JOIN history h ON p.admission_number = h.admission_number
    WHERE note LIKE '%blood pressure%' OR note LIKE '%Discharged%';
    

    让你:

    fullname = Bob
    admission_number = 3456
    
    fullname = Lucy
    admission_number = 9001
    
    fullname = Mary
    admission_number = 7828
    

    并运行以下查询:

    SELECT DISTINCT p.fullname, p.admission_number FROM patient p
    INNER JOIN history h ON p.admission_number = h.admission_number
    WHERE note LIKE '%blood pressure%';
    

    让你:

    fullname = Bob
    admission_number = 3456
    

    【讨论】:

    • 这适用于单个搜索谓词note LIKE '%blood pressure%'。问题是搜索与 AND 组合的两个谓词,例如 note == 'a' and note == 'b' - 如果我们在 WHERE 子句中执行此操作,我们将永远找不到任何记录,因为这两个测试将针对同一记录进行操作。我们想要的是找到一个相关注释 == 'a' 和另一个注释 == 'b' 的患者记录。
    【解决方案3】:

    我有一些东西 -- 使用EXISTS 来构建这些更简洁:

    select * from patients where 
        exists ( 
          select 1 from history where
            history.admission_number == patients.admission_number
            AND
            history.note LIKE '%blood pressure%'
        )
        AND
        exists (
            select 1 from history where
            history.admission_number == patients.admission_number
            AND 
            history.note LIKE '%discharged%'
            AND
            history.doctor == 'Damien'
        );
    

    好多了,现在我可以构造非常细粒度的谓词了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-07
      • 1970-01-01
      • 2021-03-30
      • 2017-07-15
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      相关资源
      最近更新 更多