多个表上每周的SQL计数摘要

时间:2017-01-25 11:02:17

标签: sql postgresql left-join

我有一个SQL,它会在过去365天内产生一系列的周数:

SELECT
  to_char(weekdate, 'YYWW') as yearWeek
FROM
GENERATE_SERIES(
  NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-365,
  NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER,
  '1 week'
) AS t(weekdate)

然后,我使用时间戳列从“team_a”表中加入每周计数摘要(“LEFT OUTER JOIN”考虑周数为0计数):

SELECT
  to_char(weekdate, 'YYWW') as yearWeek,
  count(a.timestamp) AS team_a_total
FROM
GENERATE_SERIES(
  NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-365,
  NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER,
  '1 week'
) AS t(weekdate)
LEFT OUTER JOIN team_a a
  ON to_char(weekdate, 'YYWW') = to_char(a.timestamp, 'YYWW')
GROUP BY to_char(weekdate, 'YYWW')
ORDER BY yearWeek

这个工作正常,结果如预期但我想加入另一个'team_b'表中的计数摘要,我认为这是一个简单的例子,可以添加另一个“LEFT OUTER JOIN”,如下所示:

SELECT
  to_char(weekdate, 'YYWW') as yearWeek,
  count(a.timestamp) AS team_a_total,
  count(b.timestamp) AS team_b_total
FROM
GENERATE_SERIES(
  NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-365,
  NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER,
  '1 week'
) AS t(weekdate)
LEFT OUTER JOIN team_a a
  ON to_char(weekdate, 'YYWW') = to_char(a.timestamp, 'YYWW')
LEFT OUTER JOIN team_b b
  ON to_char(weekdate, 'YYWW') = to_char(b.timestamp, 'YYWW')
GROUP BY to_char(weekdate, 'YYWW')
ORDER BY yearWeek

但结果不正确。 'team_a_total'和'team_b_total'列似乎显示了两列的乘法

例如,对于'1628'周(2016年第28周),'team_a_total'应为8,'team_b_total'为36,但两列均显示288的结果,即8x36。

我做错了什么?

感谢您的回答。基于Laurenz的代码,这对我有用(以及Hambone的答案):

---------------------------
SELECT weekdate,
  team_a_total,
  count(b.timestamp) AS team_b_total
FROM
(
  SELECT
    to_char(weekdate, 'YYWW') AS weekdate,
    count(a.timestamp) AS team_a_total
  FROM
  GENERATE_SERIES(
    NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-365,
    NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER,
    '1 week'
  ) AS t(weekdate)
  LEFT OUTER JOIN team_a a ON to_char(a.timestamp, 'YYWW') = to_char(weekdate, 'YYWW')
  GROUP BY to_char(weekdate, 'YYWW')
) subq
LEFT OUTER JOIN team_b b ON to_char(b.timestamp, 'YYWW') = weekdate
GROUP BY weekdate, team_a_total
ORDER BY weekdate
---------------------------

2 个答案:

答案 0 :(得分:2)

我认为有些子查询可能会修复它:

with a as (
  select
    to_char(timestamp, 'YYWW') as week,
    count (*) as cnt
  from team_a
  group by week
),
b as (
  select
    to_char(timestamp, 'YYWW') as week,
    count (*) as cnt
  from team_b
  group by week
),
s as (
  SELECT
    to_char(weekdate, 'YYWW') as yearWeek
  FROM
  GENERATE_SERIES(
    NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-365,
    NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER,
    '1 week'
  ) AS t(weekdate)
)
SELECT
  s.yearWeek,
  coalesce (a.cnt, 0) as team_a_total,
  coalesce (b.cnt, 0) as team_b_total
FROM
  s
  LEFT JOIN a on s.yearWeek = a.week
  left join b on s.yearWeek = b.week
ORDER BY s.yearWeek

问题是你有一个迷你笛卡尔式的加入,你在同一周加入team_ateam_b的每一条记录。{/ p>

所以,如果你有这个:

   team_a              team_b
   1633    x           1633       a
   1633    y           1633       b
   1633    z

您的加入会产生六条记录。

x-a
x-b
y-a
y-b
z-a
z-c

通过在子查询中隔离它们,您可以聚合它们,然后加入聚合结果。

子查询s并不是必需的,但我认为它使它看起来更清晰。

答案 1 :(得分:1)

执行的逻辑顺序是:首先加入,然后过滤,然后分组,然后顺序。

第一次连接后,您将获得一周的多行(分组尚未完成),如果添加第二个连接,则每个行都将连接到新表。所以你最终得到了产品。

一种方法是使用子查询强制在第二次连接之前执行第一个GROUP BY

SELECT weekdate,
       team_a_total,
       count(b.timestamp) AS team_b_total
FROM (
        SELECT to_char(weekdate, 'YYWW') AS weekdate,
               count(a.timestamp) AS team_a_total
        FROM ... t(weekdate)
           LEFT OUTER JOIN team_a ON ...
        GROUP BY to_char(weekdate, 'YYWW')
     ) subq
   LEFT OUTER JOIN team_b ON ...
GROUP BY weekdate, team_a_total
ORDER BY weekdate;