PostgreSQL 按季节分组(北半球和南半球)
PostgreSQL group by season (northern and southern hemisphere)
我的数据点遍布全球。每个数据点都有一个时间戳。
我想按季节对数据进行分组,如图所示(来源:https://en.wikipedia.org/wiki/Season#/media/File:Seasons1.svg)。
这是我目前尝试的方法(北半球 2011/2012 冬季):
SELECT * FROM my_table
WHERE my_date BETWEEN '2011-12-21' AND '2012-03-21'
AND ST_Y(ST_Centroid(geom)) > 0;
我怎样才能在所有可能的年份都这样做?我试过 ...OR date BETWEEN... 但这很慢。
怎么可能select南北半球都过冬?
更新
对于北半球的所有冬季数据,我使用此查询:
CREATE TABLE season_winter_north AS WITH first_step AS (
SELECT
id,
extract(month from my_date)::int AS month,
extract(day from my_date)::int AS day,
ST_Centroid(geom) AS geom
FROM mytable
WHERE ST_Y(ST_Centroid(geom)) > 0)
(SELECT * FROM first_step
WHERE month = 12
AND day >= 21)
UNION ALL
(SELECT * FROM first_step
WHERE month = ANY('{1,2}'))
UNION ALL
(SELECT * FROM first_step
WHERE month = 3
AND day<21)
是否有更优雅或更高效的解决方案?
with t(my_date) as (
values ('2012-07-01'::date), ('2013-03-30'), ('2013-10-12'), ('2013-01-10')
)
select
case
when my_date between
make_date(extract(year from my_date)::int, 3, 21)
and
make_date(extract(year from my_date)::int, 6, 20)
then 'spring'
when my_date between
make_date(extract(year from my_date)::int, 6, 21)
and
make_date(extract(year from my_date)::int, 9, 22)
then 'summer'
when my_date between
make_date(extract(year from my_date)::int, 9, 23)
and
make_date(extract(year from my_date)::int, 12, 20)
then 'fall'
else 'winter'
end as season,
my_date
from t
;
season | my_date
--------+------------
summer | 2012-07-01
spring | 2013-03-30
fall | 2013-10-12
winter | 2013-01-10
我想 post 这个问题的额外答案。对于此查询,无需使用函数 make_date(仅适用于 postgresql 9.4。)。
使用此 answer 中的代码,您可以为其他版本的 PostgreSQL 创建 make_date() 函数。
我也创建了这个函数,我发现速度上存在显着差异。但是我不知道,如果make_date()函数在9.4.更快。
select
case
when my_date between
(extract(year from my_date)::int||'-03-21')::date
and
(extract(year from my_date)::int||'-06-20')::date
then 'spring'
when my_date) between
(extract(year from my_date)::int||'-06-21')::date
and
(extract(year from my_date)::int||'-09-22')::date
then 'summer'
when my_date) between
(extract(year from my_date)::int||'-09-23')::date
and
(extract(year from my_date)::int||'-12-20')::date
then 'fall'
else 'winter'
end as season,
my_date
from my_table;
不带 make_date() 的查询比带 make_date.
的查询快 3 到 4 倍
更简洁,可能性能更好:
with t(my_date) as (
values ('2012-07-01'::date), ('2013-03-30'), ('2013-10-12'), ('2013-01-10') )
select
case
when to_char(my_date,'MMDD') between '0321' and '0620' then 'spring'
when to_char(my_date,'MMDD') between '0621' and '0922' then 'summer'
when to_char(my_date,'MMDD') between '0923' and '1220' then 'fall'
else 'winter'
end as season,
my_date
from t;
我的数据点遍布全球。每个数据点都有一个时间戳。
我想按季节对数据进行分组,如图所示(来源:https://en.wikipedia.org/wiki/Season#/media/File:Seasons1.svg)。
这是我目前尝试的方法(北半球 2011/2012 冬季):
SELECT * FROM my_table
WHERE my_date BETWEEN '2011-12-21' AND '2012-03-21'
AND ST_Y(ST_Centroid(geom)) > 0;
我怎样才能在所有可能的年份都这样做?我试过 ...OR date BETWEEN... 但这很慢。
怎么可能select南北半球都过冬?
更新
对于北半球的所有冬季数据,我使用此查询:
CREATE TABLE season_winter_north AS WITH first_step AS (
SELECT
id,
extract(month from my_date)::int AS month,
extract(day from my_date)::int AS day,
ST_Centroid(geom) AS geom
FROM mytable
WHERE ST_Y(ST_Centroid(geom)) > 0)
(SELECT * FROM first_step
WHERE month = 12
AND day >= 21)
UNION ALL
(SELECT * FROM first_step
WHERE month = ANY('{1,2}'))
UNION ALL
(SELECT * FROM first_step
WHERE month = 3
AND day<21)
是否有更优雅或更高效的解决方案?
with t(my_date) as (
values ('2012-07-01'::date), ('2013-03-30'), ('2013-10-12'), ('2013-01-10')
)
select
case
when my_date between
make_date(extract(year from my_date)::int, 3, 21)
and
make_date(extract(year from my_date)::int, 6, 20)
then 'spring'
when my_date between
make_date(extract(year from my_date)::int, 6, 21)
and
make_date(extract(year from my_date)::int, 9, 22)
then 'summer'
when my_date between
make_date(extract(year from my_date)::int, 9, 23)
and
make_date(extract(year from my_date)::int, 12, 20)
then 'fall'
else 'winter'
end as season,
my_date
from t
;
season | my_date
--------+------------
summer | 2012-07-01
spring | 2013-03-30
fall | 2013-10-12
winter | 2013-01-10
我想 post 这个问题的额外答案。对于此查询,无需使用函数 make_date(仅适用于 postgresql 9.4。)。
使用此 answer 中的代码,您可以为其他版本的 PostgreSQL 创建 make_date() 函数。
我也创建了这个函数,我发现速度上存在显着差异。但是我不知道,如果make_date()函数在9.4.更快。
select
case
when my_date between
(extract(year from my_date)::int||'-03-21')::date
and
(extract(year from my_date)::int||'-06-20')::date
then 'spring'
when my_date) between
(extract(year from my_date)::int||'-06-21')::date
and
(extract(year from my_date)::int||'-09-22')::date
then 'summer'
when my_date) between
(extract(year from my_date)::int||'-09-23')::date
and
(extract(year from my_date)::int||'-12-20')::date
then 'fall'
else 'winter'
end as season,
my_date
from my_table;
不带 make_date() 的查询比带 make_date.
的查询快 3 到 4 倍更简洁,可能性能更好:
with t(my_date) as (
values ('2012-07-01'::date), ('2013-03-30'), ('2013-10-12'), ('2013-01-10') )
select
case
when to_char(my_date,'MMDD') between '0321' and '0620' then 'spring'
when to_char(my_date,'MMDD') between '0621' and '0922' then 'summer'
when to_char(my_date,'MMDD') between '0923' and '1220' then 'fall'
else 'winter'
end as season,
my_date
from t;