Postgres查询将where语句的优先级设置为

时间:2018-10-12 20:18:49

标签: sql postgresql

我的查询有问题

我有2张桌子:

1) Patient
*id: number
*name: varchar

2) Patient_coverage_list:
*item_id: number
*patient_id: number
*group_id: number
*start_date: date
*end_date: date

例如:

Patient
id | Name
1  | Mat 
2  | Michael

patient_coverage_list
item_id | patient_id | group_id | start_date | end_date
1       | 1          | 12       | 2012-12-12 | 2015-12-12
2       | 1          | 12       | 2008-12-12 | 2009-12-12
3       | 1          | 12       | 2017-12-12 | 2018-12-12
4       | 1          | 15       | 2020-12-12 | 2021-12-12
5       | 2          | 12       | 2016-12-12 | 2019-12-12
6       | 2          | 11       | 2017-12-12 | 2018-12-12
7       | 2          | 12       | 2021-12-12 | 2022-12-12

我必须选择group_id等于12的所有Patient_coverage_list并使用带日期的过滤器,但是必须排除过滤器,例如:

方案1: 如果当前日期是2018-05-05,并且Patient_coverage_list看起来像这样,则显示的是介于start_date和end_date之间的行:

patient_coverage_list
item_id | patient_id | group_id | start_date | end_date
1       | 1          | 12       | 2012-12-12 | 2015-12-12
2       | 1          | 12       | 2008-12-12 | 2009-12-12
3       | 1          | 12       | 2017-12-12 | 2018-12-12
4       | 1          | 15       | 2020-12-12 | 2021-12-12

您显示的患者是:

3|1| 12 | 2017-12-12 | 2018-12-12 

方案2: 如果当前日期是2018-05-05,并且Patient_coverage_list看起来像这样, 如果当前日期在开始日期和结束日期之间,则显示下一个日期在该日期之间:

patient_coverage_list
item_id | patient_id | group_id | start_date | end_date
1       | 1          | 12       | 2012-12-12 | 2015-12-12
2       | 1          | 12       | 2008-12-12 | 2009-12-12
4       | 1          | 15       | 2020-12-12 | 2021-12-12

您显示的患者是:

4 | 1 | 15 | 2020-12-12 | 2021-12-12

方案3: 如果当前日期是2018-05-05,并且Patient_coverage_list看起来像这样, 如果当前时间在start_date和end_date之间,并且没有下一个日期,则显示带有最后日期的行:

patient_coverage_list
item_id | patient_id | group_id | start_date | end_date
1       | 1          | 12       | 2012-12-12 | 2015-12-12
2       | 1          | 12       | 2008-12-12 | 2009-12-12    

您显示的患者是:

1       | 1          | 12       | 2012-12-12 | 2015-12-12

我创建了一个查询,但我认为它会更好:

SELECT * FROM(
            SELECT pa.id, pa.name, pel.item_id, pel.patient_id, pel.group_id,  pel.start_date, pel.end_date         
            FROM patient pa 
            JOIN patient_coverage_list pel ON pel.patient_id  = pa.id 
            WHERE pel.group_id = $1 
            AND pel.item_id IN (SELECT peli.item_id 
                                FROM patient_coverage_list peli 
                                WHERE peli.patient_id = pel.patient_id 
                                AND peli.start_date < peli.end_date 
                                AND now() > peli.start_date 
                                AND now() < peli.end_date 
                                ORDER BY peli.end_date ASC 
                                LIMIT 1)
            UNION ALL
            SELECT pa.id, pa.name, pel.item_id, pel.patient_id, pel.group_id,  pel.start_date, pel.end_date         
            FROM patient pa 
            JOIN patient_coverage_list pel ON pel.patient_id  = pa.id  
            WHERE pel.group_id = $1 
            AND NOT (now() > pel.start_date AND now() < pel.end_date) 
            AND pel.start_date < pel.end_date 
            AND pel.item_id in (SELECT peli.item_id 
                                FROM seligibility.patient_eligibility_list peli 
                                WHERE peli.patient_id = pel.patient_id 
                                AND peli.start_date > now() 
                                ORDER BY peli.start_date DESC LIMIT 1) 
            AND pel.patient_id NOT IN (SELECT peli.patient_id 
                                        FROM seligibility.patient_eligibility_list peli 
                                        WHERE peli.patient_id = pel.patient_id 
                                        AND peli.start_date < peli.end_date 
                                        AND now() > peli.start_date 
                                        AND now() < peli.end_date 
                                        ORDER BY peli.end_date ASC LIMIT 1)
            UNION ALL
            SELECT pa.id, pa.name, pel.item_id, pel.patient_id, pel.group_id,  pel.start_date, pel.end_date         
            FROM patient pa 
            JOIN patient_coverage_list pel ON pel.patient_id  = pa.id   
            WHERE pel.group_id = $1 
            AND pel.start_date < pel.end_date 
            AND pel.item_id IN (SELECT peli.item_id 
                                FROM seligibility.patient_eligibility_list peli 
                                WHERE peli.patient_id = pel.patient_id 
                                AND now() > peli.end_date 
                                ORDER BY peli.end_date DESC LIMIT 1) 
            AND pel.patient_id NOT IN (SELECT peli.patient_id 
                                FROM seligibility.patient_eligibility_list peli 
                                WHERE peli.patient_id = pel.patient_id 
                                AND peli.start_date < peli.end_date 
                                AND now() > peli.start_date 
                                AND now() < peli.end_date 
                                ORDER BY peli.end_date ASC LIMIT 1) 
            AND pel.patient_id NOT IN (SELECT peli.patient_id 
                                        FROM seligibility.patient_eligibility_list peli 
                                        WHERE peli.patient_id = pel.patient_id 
                                        AND peli.start_date > now() 
                                        ORDER BY peli.start_date DESC LIMIT 1)) AS patient ORDER BY patient.id

1 个答案:

答案 0 :(得分:0)

没有理由将如此多的选择嵌套在一起。给定的方案看起来非常简单。因此,让我们从一个接一个地开始,可能在这个答案下面说明你的意思。

方案1:这似乎很简单。完全不需要从patient_coverage_list进行嵌套SELECT:

SELECT *
  FROM patient AS pa
  JOIN patient_coverage_list AS pel ON pel.patient_id = pa.id
  WHERE pel.group_id = $1
    AND pel.end_date   > CURRENT_DATE  -- use CURRENT_DATE instead of NOW() if making date comparisons
    AND pel.start_date < CURRENT_DATE
  ORDER BY pel.end_date ASC
  LIMIT 1;

方案2:根据您的示例,您实际上是在start_date之后搜索最低的CURRENT_DATE

SELECT *
  FROM patient AS pa
  JOIN patient_coverage_list AS pel ON pel.patient_id = pa.id
  WHERE pel.group_id = $1
    AND pel.start_date > CURRENT_DATE
  ORDER BY pel.start_date ASC
  LIMIT 1;

请注意,您不需要从上一个查询中过滤出结果,因为该数据集永远不会与该数据集相交(请参见start_date和CURRENT_DATE的比较)。

场景3:在这里,我们需要帮助我们解决嵌套查询,该查询告诉我们end_date之后是否有CURRENT_DATE条目。如果存在,由于AND的关系,查询返回空集,否则返回最高end_date的条目:

SELECT *
  FROM patient AS pa
  JOIN patient_coverage_list AS pel ON pel.patient_id = pa.id
  WHERE pel.group_id = $1
    AND NOT EXISTS ( SELECT *
                       FROM patient_coverage_list AS pel1
                       WHERE pel1.group_id = $1  -- important if you want to make comparison within the same group_id
                         AND pel1.end_date > CURRENT_DATE
                   )
  ORDER BY pel.end_date DESC  -- you don't mention what is meant by the "last date", if you meant start_date or end_date
  LIMIT 1;

请注意,最好使用WHERE EXISTS (SELECT * FROM ... WHERE ...)而不是WHERE id IN (SELECT id FROM ...),以便RDBMS不需要创建嵌套SELECT的数据集,然后将其与外部{{ 1}}。明智地使用嵌套的SELECT。