按列分组但每个都获得多个结果

时间:2016-08-28 13:28:47

标签: sql postgresql

我正在尝试计算过去X天每个日期response time的中位数conversations

我在下面使用以下查询,但由于某种原因,它会生成多个具有相同日期的行。

with grouping as (
        SELECT a.id, d.date, extract(epoch from (first_response_at - started_at)) as response_time
        FROM (
            select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date
            FROM generate_series(0, 2) AS offs
        ) d
        LEFT OUTER JOIN apps a on true
        LEFT OUTER JOIN conversations c ON (d.date=to_char(date_trunc('day'::varchar, c.started_at), 'YYYY-MM-DD')) and a.id = c.app_id 
        and c.app_id = a.id and c.first_response_at > (current_date - (2  || ' days')::interval)::date
      )
      select
      *
      from grouping
      where grouping.id = 'ASnYW1-RgCl0I'

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

首先,您的查询存在许多问题,假设您没有向我们展示任何部分:

  • 此查询不需要CTE。
  • 从表格apps开始,您只能使用与id相同的列c.app_id。您可以移除表apps并选择c.app_id以获得相同的结果。
  • 当您使用to_char()时,您首先不必date_trunc()dateto_char()函数会处理此问题。
  • generate_series()也适用于timestamps。只需输入带有间隔的日期值,然后在使用前将结果投放到date

所以,删除所有的flotsam我们最终得到的与你问题中的查询完全相同,但现在我们至少可以看到发生了什么。

SELECT c.app_id, to_date(d.date, 'YYYY-MM-DD') AS date, 
       extract(epoch from (first_response_at - started_at)) AS response_time
FROM generate_series(CURRENT_DATE - 2, CURRENT_DATE, interval '1 day') d(date)
LEFT JOIN conversations c ON d.date::date = c.started_at::date
                         AND c.app_id = 'ASnYW1-RgCl0I'
                         AND c.first_response_at > CURRENT_DATE - 2;

您无法计算任何地方的中间响应时间,因此这是您需要解决的一个大问题。这只需要表conversations中的数据,看起来有点像这样计算过去2天的中位响应时间:

SELECT app_id, started_at::date AS start_date,
       percentile_disc(0.5) WITHIN GROUP (ORDER BY first_response_at - started_at) AS median_response
FROM conversations
WHERE app_id = 'ASnYW1-RgCl0I'
  AND first_response_at > CURRENT_DATE - 2
GROUP BY 2;

当我们折叠两个查询,并将参数轻松地放在一个地方时,这是最终的结果:

SELECT p.id, to_date(d.date, 'YYYY-MM-DD') AS date, 
       extract(epoch from (c.median_response)) AS response_time
FROM (VALUES ('ASnYW1-RgCl0I', 2)) p(id, days)
JOIN generate_series(CURRENT_DATE - p.days, CURRENT_DATE, interval '1 day') d(date) ON true
LEFT JOIN LATERAL (
    SELECT started_at::date AS start_date,
           percentile_disc(0.5) WITHIN GROUP (ORDER BY first_response_at - started_at) AS median_response
    FROM conversations
    WHERE app_id = p.id
      AND first_response_at > CURRENT_DATE - p.days
    GROUP BY 2) c ON d.date::date = c.start_date;

如果您想更改应用的ID或要回顾的天数,您只需相应地更改VALUES子句。您还可以将整个事物包装在SQL函数中,并将VALUES子句转换为两个参数。