Sql 获取表中成对行之间的平均间隔

Sql 获取表中成对行之间的平均间隔,sql,postgresql,aggregate-functions,psql,Sql,Postgresql,Aggregate Functions,Psql,我有一个包含以下paypal交易数据的表: txn_type | date | subscription_id ----------------+----------------------------+--------------------- subscr_signup | 2014-01-01 07:53:20 | S-XXX01 subscr_signup | 2014-01-05 10:37:26

我有一个包含以下paypal交易数据的表:

    txn_type    |            date            |   subscription_id
----------------+----------------------------+---------------------
 subscr_signup  | 2014-01-01 07:53:20        | S-XXX01
 subscr_signup  | 2014-01-05 10:37:26        | S-XXX02
 subscr_signup  | 2014-01-08 08:54:00        | S-XXX03
 subscr_eot     | 2014-03-01 08:53:57        | S-XXX01
 subscr_eot     | 2014-03-05 08:58:02        | S-XXX02
我想得到给定时间段的平均订阅总长度subscr_eot是订阅的结束。对于仍在进行的“S-XXX03”订阅,我希望它从开始日期到现在都包含在平均值中。
如何在Postgres中使用SQL语句实现这一点?

我使用了两个常用的表表达式;你可以很容易地把这些碎片拆开,看看它们是干什么的

此SQL之所以复杂,原因之一是您将列名存储为数据。subscr_signup和subscr_eot实际上是列名,而不是数据。这是一个SQL反模式;希望它会给你带来很多痛苦

with subscription_dates as (
  select 
      p1.subscription_id, 
      p1.date as subscr_start,
      coalesce((select min(p2.date) 
                from paypal_transactions p2
                where p2.subscription_id = p1.subscription_id
                  and p2.txn_type = 'subscr_eot'
                  and p2.date > p1.date), current_date) as subscr_end
  from paypal_transactions p1
  where txn_type = 'subscr_signup'
), subscription_days as (
  select subscription_id, subscr_start, subscr_end, (subscr_end - subscr_start) + 1 as subscr_days
  from subscription_dates 
)
select avg(subscr_days) as avg_days
from subscription_days
-- add your date range here.

avg_days
--
75.6666666666666667

我没有将日期范围添加为WHERE子句,因为我不清楚给定时间段的含义

我使用了几个常用的表表达式;你可以很容易地把这些碎片拆开,看看它们是干什么的

此SQL之所以复杂,原因之一是您将列名存储为数据。subscr_signup和subscr_eot实际上是列名,而不是数据。这是一个SQL反模式;希望它会给你带来很多痛苦

with subscription_dates as (
  select 
      p1.subscription_id, 
      p1.date as subscr_start,
      coalesce((select min(p2.date) 
                from paypal_transactions p2
                where p2.subscription_id = p1.subscription_id
                  and p2.txn_type = 'subscr_eot'
                  and p2.date > p1.date), current_date) as subscr_end
  from paypal_transactions p1
  where txn_type = 'subscr_signup'
), subscription_days as (
  select subscription_id, subscr_start, subscr_end, (subscr_end - subscr_start) + 1 as subscr_days
  from subscription_dates 
)
select avg(subscr_days) as avg_days
from subscription_days
-- add your date range here.

avg_days
--
75.6666666666666667
我没有将日期范围添加为WHERE子句,因为我不清楚给定时间段的含义

。每个订阅的订阅长度:

select
    subscription_id,
    coalesce(t2.date, current_timestamp) - t1.date as subscription_length
from
    (
        select *
        from t
        where txn_type = 'subscr_signup'
    ) t1
    left join
    (
        select *
        from t
        where txn_type = 'subscr_eot'
    ) t2 using (subscription_id)
order by t1.subscription_id
平均数:

select
    avg(coalesce(t2.date, current_timestamp) - t1.date) as subscription_length_avg
from
    (
        select *
        from t
        where txn_type = 'subscr_signup'
    ) t1
    left join
    (
        select *
        from t
        where txn_type = 'subscr_eot'
    ) t2 using (subscription_id)
。每个订阅的订阅长度:

select
    subscription_id,
    coalesce(t2.date, current_timestamp) - t1.date as subscription_length
from
    (
        select *
        from t
        where txn_type = 'subscr_signup'
    ) t1
    left join
    (
        select *
        from t
        where txn_type = 'subscr_eot'
    ) t2 using (subscription_id)
order by t1.subscription_id
平均数:

select
    avg(coalesce(t2.date, current_timestamp) - t1.date) as subscription_length_avg
from
    (
        select *
        from t
        where txn_type = 'subscr_signup'
    ) t1
    left join
    (
        select *
        from t
        where txn_type = 'subscr_eot'
    ) t2 using (subscription_id)
使用,这将大大缩短:

SELECT avg(ts_end - ts) AS avg_subscr
FROM  (
   SELECT txn_type, ts, lag(ts, 1, localtimestamp)
                OVER (PARTITION BY subscription_id ORDER BY txn_type) AS ts_end
   FROM  t
   ) sub
WHERE txn_type = 'subscr_signup';
lag为缺少的行使用默认值。这正是我们需要的,所以我们不需要额外的

该查询建立在subscr_eot在subscr_注册之前进行排序的事实上

可能比目前提供的备选方案更快,因为它只需要一次顺序扫描——即使窗口功能增加了一些成本

使用ts列代替日期有三个原因:

你的日期实际上是一个时间戳。 日期在标准SQL中是保留字,即使在Postgres中允许。 切勿将基本类型名称用作标识符。 使用localtimestamp而不是now或current_timestamp,因为您显然是在使用

此外,列txn_type和subscription_id不应为文本 对于txn_类型可能是一个整数,对于subscription_id可能是一个整数。这将使表和索引变得更小更快

对于手头的查询,必须读取整个表。如果您需要读取性能,索引将不会有任何帮助-Postgres 9.2+中的覆盖索引除外:

CREATE INDEX t_foo_idx ON t (subscription_id, txn_type, ts);
使用,这将大大缩短:

SELECT avg(ts_end - ts) AS avg_subscr
FROM  (
   SELECT txn_type, ts, lag(ts, 1, localtimestamp)
                OVER (PARTITION BY subscription_id ORDER BY txn_type) AS ts_end
   FROM  t
   ) sub
WHERE txn_type = 'subscr_signup';
lag为缺少的行使用默认值。这正是我们需要的,所以我们不需要额外的

该查询建立在subscr_eot在subscr_注册之前进行排序的事实上

可能比目前提供的备选方案更快,因为它只需要一次顺序扫描——即使窗口功能增加了一些成本

使用ts列代替日期有三个原因:

你的日期实际上是一个时间戳。 日期在标准SQL中是保留字,即使在Postgres中允许。 切勿将基本类型名称用作标识符。 使用localtimestamp而不是now或current_timestamp,因为您显然是在使用

此外,列txn_type和subscription_id不应为文本 对于txn_类型可能是一个整数,对于subscription_id可能是一个整数。这将使表和索引变得更小更快

对于手头的查询,必须读取整个表。如果您需要读取性能,索引将不会有任何帮助-Postgres 9.2+中的覆盖索引除外:

CREATE INDEX t_foo_idx ON t (subscription_id, txn_type, ts);

是否缺少开始日期或结束日期?开始日期为subscr_注册。例如S-XXX01开始日期是2014-01-01,结束日期是2014-03-01哦,对不起,我误解了。如果你说的是日期范围,它可以是任何东西,例如2014-01-01-现在。请你发布预期输出…预期输出将是订阅活动的平均天数。在这种情况下,如果我们的日期范围为2014-01-01至2014-04-24,则平均订阅时间为75.7天60天+60天+107天/3次订阅是否缺少开始日期或结束日期?开始日期为subscr_注册。例如S-XXX01开始日期是2014-01-01,结束日期是2014-03-01哦,对不起,我误解了。如果你说的是日期范围,它可以是任何东西,例如2014-01-01-现在。请你发布预期输出…预期输出将是订阅活动的平均天数。在这种情况下,如果我们的日期范围是2014-01-01至2014-04-24,则平均订阅时间为75.7天60天+60天+107天/3次订阅,我将发布类似的内容,但这一点更清楚。很好。我也一样该死的,从工作到家的地铁+谢谢你!这正是我所看到的
纳什。我不知道合并功能。非常有帮助。我正要发布类似的内容,但这更清楚。很好。我也一样该死的,从工作到家的地铁+谢谢你!这正是我想要的。我不知道合并功能。非常有用。