包含 WITH 子查询的 PostgreSQL 查询

PostgreSQL query including WITH subquery

从以下 table(名为 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 是必不可少的吗?


创建 table 进行测试:

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') ;

你可以用 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.

您的子查询包含一个 GROUP BY 表达式,whlist 外部查询没有。所以你必须使用子查询。

您可以将其内联,但这只会使查询更难阅读。您的查询尽可能简单。

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

而不是 select 在您的 CTE 中输入城镇,select gid 列。然后加入原来的 table 就可以了,瞧:

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 一起增长时有效。 否则您需要回退到您发布的原始查询和最大日期。但是你 can/should 使用 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