PostgreSQL查询包括WITH子查询

时间:2016-07-20 11:58:10

标签: sql postgresql greatest-n-per-group

从下表(名为status)中,我需要在2015年底之前提取状态为'01'的城镇代码。列status_date存储日期一个小镇改变了它的地位。

gid  | town      | status  | status_date
-----+-----------+---------+-------------
1    | 86001     | 00      | 2000-01-01
2    | 86001     | 01      | 2016-03-01
3    | 86002     | 01      | 2000-01-01
4    | 86003     | 00      | 2000-01-01
5    | 86003     | 01      | 2015-03-01
6    | 86003     | 02      | 2015-09-01

我可以使用以下有点长的查询来实现:

WITH tab AS (SELECT town, MAX(status_date) FROM status GROUP BY town)

  SELECT 
    t.town

  FROM tab t
    LEFT JOIN status s ON t.town = s.town AND t.max = s.status_date 

  WHERE t.max < '2016-01-01' AND s.status = '01' ; 

结果是:

town   
-------
86002

有关如何使此查询更简单的任何想法? WITH是否必不可少?

创建用于测试的表:

CREATE TABLE status (gid serial NOT NULL, town CHARACTER VARYING(5), status CHARACTER VARYING(2), status_date DATE) ;

INSERT INTO status (town, status, status_date) VALUES
  ('86001', '00', '2000-01-01'),
  ('86001', '01', '2016-03-01'),
  ('86002', '01', '2000-01-01'),
  ('86003', '00', '2000-01-01'),
  ('86003', '01', '2015-03-01'),
  ('86003', '02', '2015-09-01') ;

3 个答案:

答案 0 :(得分:3)

您可以使用distinct on

执行此操作
select s.*
from (select distinct on (s.town) s.*
      from status s
      where s.status_date < '2016-01-01'
      order by s.town, s.status_date desc
     ) s
where status = '01';

此查询将获取截至2015年底的每个城镇的最新状态。然后,外部查询将选择01

答案 1 :(得分:0)

您的子查询包含GROUP BY表达式,外部查询不包含该表达式。所以你必须使用子查询。

您可以内联,但这只会使查询更难以阅读。您的查询非常简单。

答案 2 :(得分:0)

您的方法是正确的 - CTE驱动的查询更具可读性,如果构建正确,它们可以提供性能优势。

不要在CTE中选择城镇,而是选择gid列。然后加入原始表格,然后瞧:

WITH tab AS (
    SELECT gid, MAX(status_date) 
    FROM status 
    GROUP BY gid 
    HAVING MAX(status_date) < '2016-01-01'
)
SELECT s.whatever
FROM tab t
    INNER JOIN status s ON t.gid = s.sid
WHERE s.status = '01' 

修改

道歉;今天早上我很匆忙,所以我写了错误的查询。现在我有时间更深入地分析问题。

如果表现很重要,那么就像这样在PK周围解决。

WITH tab AS (
    SELECT MAX(gid) as ID
    FROM [status]
    WHERE YEAR(status_date) = 2015 AND status = '01'
    GROUP BY town
)
SELECT s.*
FROM tab t INNER JOIN status s ON t.ID = s.gid

仅当status_dategid一起增长时才有效。此外,您需要回退到您发布的原始查询,最大日期。但是,您可以/应该使用INNER JOIN代替LEFT JOIN

WITH tab AS (  
    SELECT town, MAX(status_date) as Latest
    FROM [status]
    WHERE YEAR(status_date) = 2015 AND status = '01'
    GROUP BY town
)
SELECT s.*
FROM tab t INNER JOIN [status] s ON t.town = s.town AND t.Latest = s.status_date