【问题标题】:Select Null in With Statement在 With 语句中选择 Null
【发布时间】:2015-03-17 08:43:30
【问题描述】:

我在 SQLWorkbenchJ 中使用 PostgreSQL,但我很苦恼。

我有一个WITH 语句,它根据行号选择日期。如果语句找不到行号,我想在日期字段中选择 null。这目前不会发生,它只是选择所有字段都不为空的记录。我假设它与连接有关,但我不确定。

当前声明如下。它应该返回大约 50,000 条记录,但目前返回的记录不到 2000 条。

WITH FifthEnquiry AS
(
  SELECT emailaddress,
         SentDate,
         ROW_NUMBER() OVER (PARTITION BY emailaddress ORDER BY COUNT(*) DESC) AS rk
  FROM SentEmails
  GROUP BY emailaddress,
           SentDate
),
TenthEnquiry AS
(
  SELECT emailaddress,
         SentDate,
         ROW_NUMBER() OVER (PARTITION BY emailaddress ORDER BY COUNT(*) DESC) AS rk
  FROM SentEmails
  GROUP BY emailaddress,
           SentDate
),
TwentiethEnquiry AS
(
  SELECT emailaddress,
         SentDate,
         ROW_NUMBER() OVER (PARTITION BY emailaddress ORDER BY COUNT(*) DESC) AS rk
  FROM SentEmails
  GROUP BY emailaddress,
           SentDate
)
SELECT FifthEnquiry.emailaddress,
       FifthEnquiry.SentDate AS Fith,
       TenthEnquiry.SentDate AS Tenth,
       TwentiethEnquiry.SentDate AS Twentieth,
FROM FifthEnquiry
  JOIN TenthEnquiry ON FifthEnquiry.emailaddress = TenthEnquiry.emailaddress
  JOIN TwentiethEnquiry ON FifthEnquiry.emailaddress = TwentiethEnquiry.emailaddress
WHERE (FifthEnquiry.rk = 5)
AND   (TenthEnquiry.rk = 10)
AND   (TwentiethEnquiry.rk = 20)

【问题讨论】:

  • 根据需要使用左连接或右连接。
  • 两者都给出相同数量的记录@FrankHeikens

标签: sql postgresql select null common-table-expression


【解决方案1】:

您可以在很大程度上简化。并使用LEFT JOIN 保留GROUP BY 之后至少5 行的所有电子邮件地址,即使没有第10 行或第20 行:

WITH cte AS (
   SELECT emailaddress, SentDate,
          ROW_NUMBER() OVER (PARTITION BY emailaddress
                             ORDER BY COUNT(*) DESC, SentDate) AS rn
   FROM   SentEmails
   GROUP  BY 1,2
   )
SELECT enq05.emailaddress,
       enq05.SentDate AS fifth,
       enq10.SentDate AS tenth,
       enq20.SentDate AS twentieth
FROM        cte AS enq05
LEFT   JOIN cte AS enq10 ON enq10.emailaddress = enq05.emailaddress
                        AND enq10.rn = 10
LEFT   JOIN cte AS enq20 ON enq20.emailaddress = enq05.emailaddress
                        AND enq20.rn = 20
WHERE  enq05.rn = 5;
  • 您不需要单独的 CTE,所有三个都在做同样的事情。 一个 CTE 就足够了,而且显然更快。在外部查询中使用具有不同表别名的自联接。

  • 由于我们现在使用的是 LEFT JOIN,所以我们是否在 JOIN 或 WHERE 子句中添加附加条件很重要。 WHERE 子句中的条件有效地强制 Postgres 将连接视为普通的[INNER] JOIN。我相应地将条件移至 JOIN 子句。详情:

  • 使用rn,而不是rk 作为列别名。这是一个“行号”,而不是“等级”。请注意row_number() and rank() 之间的重要差异

  • SentDate 添加到ORDER BY 作为(emailaddress, SentDate) 的决胜局,具有相同的计数以获得稳定的排序顺序。我拥有SentDate IS NULL 的方式在每组中排在最后。您可能希望使用NULLS LAST 进行降序排序(不适用于COUNT(*),它永远不会为NULL):

  • 您需要注意一个更微妙的细节:tenthtwentieth 在结果中都可以为 NULL,两个不同的原因,如果 SentDate 可以为 NULL在基础表中。结果中tenth 的NULL 值可能意味着emailaddress 的不同值少于10 个,或者可能意味着NULL 根据您的排序顺序位于第10 位。

【讨论】:

  • 我仍然遇到与此查询相同的问题。很高兴它可以缩短。我仍然没有在任何列中看到“null”。如果有意义,它只会选择至少有 20 行的电子邮件地址
  • @ChrisHall:你测试了我的最新版本吗?我的初稿仍然有 WHERE 条件强制将 LEFT JOIN 转换为普通 JOIN。当前版本肯定包含至少 5 行的 all 电子邮件地址(分组后!!)。您确实想在 分组之后对行进行编号,对吗?因为这就是你现在正在做的事情。
  • @ChrisHall:我添加了更多指针。这个查询有一些棘手的方面。
猜你喜欢
  • 1970-01-01
  • 2020-12-13
  • 1970-01-01
  • 2016-08-05
  • 1970-01-01
  • 2013-07-31
  • 1970-01-01
  • 2019-02-14
  • 1970-01-01
相关资源
最近更新 更多