包含 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_date
与 gid
一起增长时有效。 否则您需要回退到您发布的原始查询和最大日期。但是你 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
从以下 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_date
与 gid
一起增长时有效。 否则您需要回退到您发布的原始查询和最大日期。但是你 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