PostgreSQL group by season (northern and southern hemisphere)

北慕城南 提交于 2019-12-24 03:48:10

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!