问题
I have data points distributed over the whole globe. Every data point has a timestamp.
I want to group the data by the seasons, as shown in this picture (source: https://en.wikipedia.org/wiki/Season#/media/File:Seasons1.svg).
This is what I tried so far (winter 2011/2012 in the northern hemisphere):
SELECT * FROM my_table
WHERE my_date BETWEEN '2011-12-21' AND '2012-03-21'
AND ST_Y(ST_Centroid(geom)) > 0;
How can I do this for all possible years? I tried ...OR date BETWEEN... but this is very slow.
How is it possible to select both winter in the northern and southern hemisphere?
Update
For all winter data on the northern hemisphere I use this query:
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)
Is there a more elegant or efficient solution?
回答1:
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
回答2:
More succinctly, and probably with better performance:
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;
回答3:
I want to post this additional answer to my question. With this query there is no need to use the function make_date (only available with postgresql 9.4.).
With the code from this answer your able to create a make_date() function for other versions of PostgreSQL.
I have created the function as well and I recognized a significant difference in speed. But I don't know, if the make_date() function in 9.4. is faster.
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;
The query without make_date() is about 3 to 4 times faster then with make_date.
来源:https://stackoverflow.com/questions/32393295/postgresql-group-by-season-northern-and-southern-hemisphere