我该如何编写这个SQL查询?

时间:2010-09-17 19:20:06

标签: sql database postgresql

我有以下表格:

PERSON_T              DISEASE_T               DRUG_T
=========             ==========              ========
PERSON_ID             DISEASE_ID              DRUG_ID
GENDER                PERSON_ID               PERSON_ID
NAME                  DISEASE_START_DATE      DRUG_START_DATE
                      DISEASE_END_DATE        DRUG_END_DATE

我想编写一个查询,该查询接受疾病ID的输入,并为数据库中的每个人返回一行,其中包含性别列,是否曾患有该疾病的列,以及列对于每种药物,它指明在感染疾病之前是否服用了这种药物。 I.E. true表示drug_start_date< disease_start_date。 False意味着drug_start_date> disease_start_date或该人从未接受过该特定药物。

我们目前从数据库中提取所有数据并使用Java创建包含所有这些值的2D数组。我们正在研究将此逻辑移入数据库。是否可以创建一个将根据需要返回结果集的查询,或者我是否必须创建存储过程?我们正在使用Postgres,但我认为另一个数据库的SQL答案很容易转换为Postgres。

2 个答案:

答案 0 :(得分:3)

根据提供的信息:

   SELECT p.name,
          p.gender,
          CASE WHEN d.disease_id IS NULL THEN 'N' ELSE 'Y' END AS had_disease,
          dt.drug_id
     FROM PERSON p
LEFT JOIN DISEASE d ON d.person_id = p.person_id
                   AND d.disease_id = ?
LEFT JOIN DRUG_T dt ON dt.person_id = p.person_id
                   AND dt.drug_start_date < d.disease_start_date

..但是除了drug_id列之外,会有很多行看起来重复。

答案 1 :(得分:1)

您基本上希望使用药物创建cross-tab查询。虽然有很多OLAP工具可以做这种事情(在各种其他切片和切割数据中),在传统的SQL中做这样的事情并不容易(而且,一般来说,不可能没有除了最简单的场景之外的所有程序句法。)

使用SQL执行此操作时,实际上有两个选项(更准确地说,您有一个选项,另一个更复杂但更灵活的选项来自它):

  1. 在查询中使用一系列CASE语句,以生成代表每种药物的列。这要求提前知道变量值列表(即药物)
  2. 使用过程SQL语言(如T-SQL)动态构造一个使用上述case语句的查询,同时从数据本身获取该值列表。
  3. 这两个选项基本上做同样的事情,你只是简单易用,易于维护,灵活性在第二个选项。

    例如,使用选项1:

    select
        p.NAME,
        p.GENDER,
        (case when d.DISEASE_ID is null then 0 else 1 end) as HAD_DISEASE,
        (case when sum(case when dr.DRUG_ID = 1 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_1,
        (case when sum(case when dr.DRUG_ID = 2 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_2,
        (case when sum(case when dr.DRUG_ID = 3 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_3
    
    from PERSON_T p
    
    left join DISEASE_T d on d.PERSON_ID = p.PERSON_ID and d.DISEASE_ID = @DiseaseId
    left join DRUG_T dr on dr.PERSON_ID = p.PERSON_ID and dr.DRUG_START_DATE < d.DISEASE_START_DATE
    
    group by p.PERSON_ID, p.NAME, p.GENDER, d.DISEASE_ID
    

    正如你所知道的,当你超出一些潜在价值时,这会变得有点费力。

    另一种选择是动态构造此查询。我不知道PostgreSQL及其具有的程序功能(如果有的话),但整个过程将是这样的:

    1. 收集潜在DRUG_ID值列表以及列
    2. 的名称
    3. 准备三个字符串值:SQL前缀(第一个与药物相关的CASE语句之前的所有内容,SQL stuffix(最后一个与药物相关的CASE语句之后的所有内容)和动态部分< / LI>
    4. 通过基于先前检索的列表
    5. 组合药物CASE语句来构造动态部分
    6. 将它们组合成一个(希望是有效的)SQL语句并执行