4

I have the following table in the postgres database (the table name is table_test):

          id             dia          Data_sensor_Analog
         2165         2020-09-20       4585542
         2165         2020-09-21       4954566
         2165         2020-09-26           255 

I would like to count how many consecutive days have the attribute dia.

For this, I tried to make the following code:

           WITH 

           groups AS (
           SELECT
              ROW_NUMBER() OVER (ORDER BY dia) AS rn,
              dateadd(dia, -ROW_NUMBER() OVER (ORDER BY dia), dia) AS grp,
              dia
           FROM table_test
          )

          SELECT
          COUNT(*) AS consecutiveDates,
          MIN(dia) AS minDate,
          MAX(dia) AS maxDate
          FROM groups
          GROUP BY grp
          ORDER BY 1 DESC, 2 DESC

I would like the output to be:

             consecutiveDates       minDate        maxDate  
                     1            2020-09-20      2020-09-21

However, when I run the code, the following error message appears:

          ERROR:  function dateadd(text, bigint, text) does not exist
          LINE 17:       dateadd(dia, -ROW_NUMBER() OVER (ORDER BY dia), dia) A

I'm using postgres and found this sample code on the website: https://blog.jooq.org/2015/11/07/how-to-find-the-longest-consecutive-series-of-events-in-sql/

I transformed the dia attribute to:

         ALTER TABLE table_test
         ALTER COLUMN dia
         TYPE TIMESTAMP WITHOUT TIME ZONE
         USING dia::timestamp without time zone;
1
  • Postgresql functions are typed. If some argument in the call has the wrong type you will get this error. Find function definition and compare, then cast the argument to meet the function signature. Mainly it probably takes date time instead of text as input. Commented May 18, 2021 at 15:05

3 Answers 3

5

Considering you have only one entry for a day in your table then try this:

select id, count(*) -1 "count", max(dia), min(dia) from (
select *, 
date(dia) - row_number() over (partition by id order by date(dia)) * interval '1 day' "filter" 
from table_test
) t1 
group by id, filter
having count(*) -1 > 0

DEMO

In case you have multiple values for same date then try below:

with cte as (
select 
*,
date(dia) date_,date(dia) - dense_rank() over ( partition by id order by date(dia)) * interval '1 day' "filter" 
from table_test
)
select 
id, count(distinct date_) -1 "count" , max(dia),min(dia) 
from cte
group by id, filter
having count(distinct date_) -1 >0

DEMO

Sign up to request clarification or add additional context in comments.

3 Comments

Worked perfectly! In the original table, with the same structure as the "table_test" table, I have other identifiers (id). The question is how would you count consecutive days by id?
Its a minor change in dense_rank() or row_number() . updated the answer as per your requirement
Worked perfectly!
1

You can subtract an enumerated value, but you need a subquery or CTE:

select min(dia), max(dia), count(*)
from (select t.*,
             row_number() over (order by dia) as seqnum
      from table_test t
     ) t
group by dia - seqnum * interval '1 day';

However, it looks like dia is a string and not a date. To address that:

group by (dia::date) - seqnum * interval '1 day';

The format is fine for conversion to a date.

Here is a db<>fiddle.

2 Comments

I added a type transformation to the "dia" attribute in the question. Your code has executed. However, the answer doesn't just show the desired output. It is showing the "dia" 2020-09-26 as max and min and with count = 1. Do you know how I can solve it?
@JaneBorges . . . Of course. If you just want rows with more than one date, add having count(*) > 1. Your question isn't really clear on what results you want.
0

PostrgeSQL doesn't support dateadd, here's the official docs: https://www.postgresql.org/docs/9.4/functions-datetime.html

So, for PG, the solution looks like:

       WITH 
dates(dia) AS (
    SELECT DISTINCT CAST(dia AS DATE)
     FROM table_test
  ),
  groups AS (
    SELECT
      ROW_NUMBER() OVER (ORDER BY dia) AS rn,
      (dia - make_interval(days :=  cast (ROW_NUMBER() OVER (ORDER BY dia will) AS INTEGER )  )) AS grp,
      date
    FROM dates
      )
 SELECT 
   COUNT(*) AS streak,
   MIN(date) AS startDate,
   MAX(date) AS endDate
 FROM groups
 GROUP BY grp
 ORDER BY 1 DESC, 2 DESC;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.