sqlpostgresqlselectaggregate-functionsrange-types

Count visits on a daily base with one select


I have this table in my PostgreSQL:

CREATE TABLE visits(
id BIGSERIAL NOT NULL PRIMARY KEY,
timeslot TSRANGE NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id),
CONSTRAINT overlapping_timeslots EXCLUDE USING GIST (
    user_id WITH =,
    timeslot WITH &&
));

With this Data:

|   id  |   timeslot                                |   user_id |
|   1   |   10.02.2014 10:00 - 10.02.2014 17:00     |   2       |
|   2   |   10.02.2014 18:00 - 10.02.2014 19:00     |   2       |    
|   3   |   11.02.2014 01:00 - 11.02.2014 02:00     |   2       |
|   4   |   10.02.2014 12:00 - 11.02.2014 17:00     |   2       |
|   5   |   11.02.2014 12:00 - 11.02.2014 12:30     |   2       |

I need to know how many users are visted my shop every day. If a user visits the shop twice a day it should be count twice.

In the example above it should be.

Users at 10.02 = 3 (ID: 1,2,4)
Users at 11.02 = 3 (ID: 3,4,5)

Solution

  • Assuming an arbitrary period of time for lack of definition for "every day":

    SELECT  day, count(*) AS visits, array_agg(id) AS ids
    FROM    generate_series ('2014-02-10'::date
                           , '2014-02-12'::date
                           , interval '1 day') AS d(day)
    JOIN    visits ON tsrange(day::timestamp
                            , day::timestamp + interval '1 day') && timeslot
    GROUP   BY 1;
    

    && is the "overlap" operator for range types.

    Use a LEFT JOIN to include days with 0 visits in the result.

    -> SQLfiddle demo.