【问题标题】:Combine a CROSS JOIN and a LEFT JOIN组合一个 CROSS JOIN 和一个 LEFT JOIN
【发布时间】:2019-09-12 17:18:23
【问题描述】:

我有两个名为 authorcommit_metrics 的表。他们都有一个id 字段。作者有author_nameauthor_email。 Commit_metrics 有author_idauthor_date

我正在尝试编写一个查询,该查询将获取每个作者在给定一周内的提交次数,即使该数字为 0。到目前为止,这是我所拥有的:

SELECT a.id, a.author_name, a.author_email, c.week_num, COUNT(c.id)
FROM author AS a
     CROSS JOIN generate_series(1, 610) AS s(n)
     LEFT JOIN  (SELECT c.id,
                        c.author_id,
                        c.author_date,
                        WEEK_NUMBER(c.author_date) AS week_num
                 FROM commit_metrics c) AS c ON s.n = c.week_num AND a.id = c.author_id
WHERE c.week_num IS NOT NULL
GROUP BY a.id, a.author_name, a.author_email, c.week_num
ORDER BY c.week_num DESC, a.author_name;

WEEK_NUMBER 是我为此查询编写的函数:

CREATE OR REPLACE FUNCTION WEEK_NUMBER(date TIMESTAMP) RETURNS INTEGER AS
$$
SELECT TRUNC(DATE_PART('day', date - '2008-01-01') / 7)::INTEGER;
$$ LANGUAGE SQL;

目前,该查询的工作方式类似于具有一个主要警告的魅力。当作者在给定的一周内没有提交时,它不会正确计算 0。我不确定为什么没有。当我只使用FROMCROSS JOIN 进行查询时,它会正确打印数千个联合作者/周。但是,当我添加 LEFT JOIN 时,它会丢失作者未提交的任何一周。

任何帮助将不胜感激。如果没有必要,我愿意取消generate_series 电话。

另外,我发现了this 的帖子,但我认为这对我的情况没有帮助。

【问题讨论】:

  • 顺便说一句,您不需要创建函数来计算周数。 PostgreSQL 已经有一个功能。 stackoverflow.com/questions/34050103/…
  • 了解 LEFT JOIN ON 返回的内容:INNER JOIN ON 行 UNION ALL 不匹配的左表行,由 NULL 扩展。作为 OUTER JOIN 的一部分,始终知道您想要什么 INNER JOIN。 WHERE 或 INNER JOIN ON 在 OUTER JOIN ON 删除任何由 NULL 扩展的行后,需要右 [sic] 表列不为 NULL,即只留下 INNER JOIN ON 行,即“将 OUTER JOIN 转换为 INNER JOIN”。你有那个。
  • @philipxy 尽管使用 SQL 多年,但我以前从未需要过 CROSS JOIN。因为它是我使用的新东西,所以我假设我的错误比我更高级别,只是作为一个假人并使用LEFT JOIN 而不是CROSS JOIN 进行过滤。简单地删除WHERE 子句实际上并不能解决我的问题,因为我需要将SELECT 更改为使用s.n 而不是c.week_num。该链接(和您的评论)会帮助我到达那里,但这不是完全重复。你会同意/不同意吗?如果您同意,您可以取消标记我的问题吗?干杯:)
  • 我为一个明显的问题提供了一个欺骗链接。 (将测试从 where 移动到 on不是一个神奇的修复,这只是猜测而不是解释的糟糕答案。我们通常不知道修复,因为对于以where 结尾的子表达式没有明确的预期规范--例如在这里。)但是由于缺少minimal reproducible example 来确定我们的第一个错误点,这篇文章仍然应该关闭(在明确之前阻止答案)可以说是“的”问题。欺骗确实适用,甚至是最深/第一个错误,我无法更改(仅放弃)近距离投票,并且在您给出 MRE 之前,有必要进行一些近距离投票。

标签: sql postgresql cross-join


【解决方案1】:

虽然您使用的是左连接,但“WHERE c.week_num IS NOT NULL”会过滤掉所有没有帖子的情况。试试这个:

SELECT a.id, a.author_name, a.author_email, s.n as week_num, COUNT(c.id) as post_count
FROM author AS a
     CROSS JOIN generate_series(1, 610) AS s(n)
     LEFT JOIN  (SELECT c.id,
                        c.author_id,
                        c.author_date,
                        WEEK_NUMBER(c.author_date) AS week_num
                 FROM commit_metrics c) AS c ON s.n = c.week_num AND a.id = c.author_id
GROUP BY a.id, a.author_name, a.author_email, s.n
ORDER BY s.n DESC, a.author_name;

【讨论】:

  • 你不需要合并,COUNT() 永远不会返回 null。
【解决方案2】:

您的WHERE 子句排除了commit_metrics 上为空的记录,即作者在所选周内没有提交的情况。您应该从 WHERE 子句中删除它以获得所需的输出。

如果您需要WHERE 子句根据您的数据消除一些CROSS JOIN 记录,您需要将CROSS JOINWHERE 放在您LEFT JOIN 的子选择中,或者在当前的WHERE 子句中创建一些更复杂的逻辑。

【讨论】:

    【解决方案3】:

    删除过滤条件。也不需要子查询,您想选择s.n 而不是c.week_num

    SELECT a.id, a.author_name, a.author_email, s.n as week_num, COUNT(c.id)
    FROM author a CROSS JOIN
         generate_series(1, 610) AS s(n) LEFT JOIN
         commit_metrics c
         ON s.n = WEEK_NUMBER(c.author_date) AND a.id = c.author_id
    GROUP BY a.id, a.author_name, a.author_email, c.week_num
    ORDER BY c.week_num DESC, a.author_name;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-14
      • 2019-09-03
      • 1970-01-01
      • 2016-09-15
      • 1970-01-01
      • 1970-01-01
      • 2015-11-03
      • 2012-03-15
      相关资源
      最近更新 更多