Sql-запрос для поиска пользователей, которые открыли электронные письма между 2-м и 5-м днем после отправки электронной почты

#sql #postgresql

#sql #postgresql

Вопрос:

у меня есть таблица, подобная этой.пример данных

  ---------------------------- --------------------------- --------- ---------- 
|id                          |email                      |event    |timestamp |
 ---------------------------- --------------------------- --------- ---------- 
|take-2-blazin-it-safe-us-can|1297323246@yahoo.com       |processed|1584974510|
|take-2-blazin-it-safe-us-can|1297323246@yahoo.com         |delivered|1584974513|
|take-2-blazin-it-safe-us-can|1297323246@yahoo.com       |open     |1584978453|
|take-2-blazin-it-safe-us-can|1297323246@yahoo.com     |open     |1584983574|
|take-2-blazin-it-safe-us-can|1297323246@yahoo.com        |open     |1584983578|
 ---------------------------- --------------------------- --------- ---------- 
  

я не хочу, чтобы пользователи, которые открывали электронные письма между 2-5 днями после доставки электронной почты. Пример: если электронное письмо отправлено 21-м, то из пользователей, которые открыли электронную почту между 23-м и 26-м, включая как 23-й, так и 26-й.

До сих пор я пробовал так

 with tab as(
  select  distinct * from table
  where event in ('delivered','open')
  )
select count(case when last_val - first_val between 2 and 5 then 1 else 0 ) from(
select *, first_value(from_unixtime(timestamp,'yyyy-MM-dd') over(partition by id,email order by timestamp asc) as first_val,
last_value(from_unixtime(timestamp,'yyyy-MM-dd') over(partition by id,email order by timestamp asc) last_val from tab

)
  

Но это сработает, только если пользователь открыл электронное письмо один раз, но бывают случаи, когда пользователь открывал электронное письмо более одного раза. Это количество также должно быть включено.

Есть ли какой-либо чистый способ сделать это?

Ответ №1:

Вы можете использовать агрегацию, но сначала получите временную метку доставки. Следующее просто использует арифметику временных меток, а не преобразование в значения даты / времени:

 select id, email
from (select t.*,
             min(timestamp) filter (where event = 'delivered') over (partition by id, email) as delivered_timestamp
      from t
     ) t
where event = 'open'
group by id, email
having count(*) filter (where timetamp >= delivered_timestamp   2*24*60*60 and
                              timestamp < delivered_timestamp   6*24*60*60
                       ) > 0;
  

Другой метод exists :

 select t.*
from t
where t.event = 'delivered' and
      exists (select 1
              from t t2
              where t2.id = t.tid and t2.email = t.email and
                    t2.timestamp >= t.timestamp   2*24*6*60 and
                    t2.timestamp < t.timestamp   6*24*6*60
             );
  

Комментарии:

1. Спасибо, это действительно сработает, но что, если мы хотим, чтобы пользователи, которые открылись через 10 дней, также отвечали на тот же запрос, нужно ли нам писать отдельный запрос

2. @MaheshKalani . . . (1) Это отвечает на вопрос, который вы задали здесь. (2) В этом ответе есть несколько вариантов, некоторые из них могут быть адаптированы к нескольким временным рамкам.

3. range between 2*24*60*60 following and 6*24*60*60 - 1 following это не работает с posgressql. выдает ошибку SYNTAX_ERROR: строка 4:19: Следующий ДИАПАЗОН рамок окна поддерживается только с НЕОГРАНИЧЕННЫМ

4. @yahoo . . . Я удалил эту опцию. Я забыл, что Postgres не полностью реализует стандартную функциональность — обычно это довольно согласуется со стандартным SQL.