重构 postgres join vs except

Posted

技术标签:

【中文标题】重构 postgres join vs except【英文标题】:refactoring postgres join vs except 【发布时间】:2016-08-26 09:31:58 【问题描述】:

我正在尝试重构我的一个查询,但我做的不是很正确。

我想合并两个查询并创建一个,但我对它如何与 LEFT JOIN 一起使用感到困惑。

所有处于“活跃”状态的 QuizMasters

减号 (-)

在某一天有“活跃”事件的测验大师(并非所有测验大师只有约 25% 的事件)。

定义

Events 存储给定事件的 start_at dow/wday,例如周一至周日(尽管是 DateTime,但只有 wday 和时间是相关的)。 EventsQuizMasters 具有 “活动” 或非活动状态。

旧查询(哪些数据正确)

SELECT first_name, last_name, email
FROM quiz_masters
WHERE quiz_masters.state = 'active' # (175 rows)

EXCEPT

SELECT first_name, last_name, email
FROM quiz_masters
LEFT JOIN events ON events.quiz_master_id = quiz_masters.id
WHERE quiz_masters.state = 'active'
AND EXTRACT(dow FROM events.start_at::timestamp::date) = 3 AND events.state = 'active'
GROUP BY first_name, last_name, email # (- 20 rows)

共有 155 行与查询匹配。

组合查询不起作用

我想将它们组合成类似的东西:

SELECT first_name, last_name, email
FROM quiz_masters
LEFT JOIN events ON events.quiz_master_id = quiz_masters.id
WHERE quiz_masters.state = 'active'
AND events.quiz_master_id IS null
OR (EXTRACT(dow FROM events.start_at::timestamp::date) <> 3 AND events.state = 'active')
GROUP BY first_name, last_name, email

144 行(缺少 11 行)

但我不确定如何保留一些quiz_masters 的所有行,这些quiz_masters 处于活动状态但没有任何事件。它仍然会删除它们。也许我需要其他类型的加入?

【问题讨论】:

except 通常可以重写为not exists 条件。 可能在缺少行的情况下(extract ....) 为假并且事件中的行存在?您当前在哪里 (quiz_masters.state = 'active' AND events.quiz_master_id IS null) OR (EXTRACT ...)事件中没有行 或(dow3 和 events.state=active) 顺便说一句 ... left join events on ... where events. ... 是错误的。即使您执行events.quiz_master_id IS null 检查,它实际上也是inner join quiz_masters.state = 'active' 所在的quiz_masters.state = 'active' 中是否有记录但events 表中没有对应记录? 【参考方案1】:

在第一个查询中,您排除了周三活跃的所有事件,因此包括了任何一天的非活跃事件。在组合查询中,您包括在除星期三以外的任何一天都处于活动状态的所有事件,并且没有任何非活动事件。这就是你的 11 行差异。

这应该会让你回到 155 行:

SELECT DISTINCT first_name, last_name, email
FROM quiz_masters
LEFT JOIN (
    SELECT quiz_master_id AS id, state
    FROM events
    WHERE EXTRACT(dow FROM events.start_at::timestamp) = 3
    AND events.state = 'active') ev USING (id)
WHERE quiz_masters.state = 'active'
AND ev.state IS NULL;

显然,您的测验大师有多个条目,但您应该选择 DISTINCT 行而不是 GROUP BYGROUP BY 只能与聚合函数一起使用。

【讨论】:

这不太行。它返回 82 行而不是 155 行;-( 并非所有 quiz_masters 都有一个事件,我认为一旦添加了 AND NOT,它就会删除这些事件? 您能否编辑您的问题并给出表格定义并用自然语言编写您想要检索的数据?那会容易得多。另请参阅重新编写的查询。 完美!谢谢。【参考方案2】:

使用人类语言。

第一个查询:

quiz_masters 中删除在dow = 3 处具有活动事件的所有条目

第二次查询:

quiz_masters 中选择在dow &lt;&gt; 3 有活动活动的条目

一般情况下没有平等的条件。例如,如果 quiz_masters 同时在 dow = 3 和 dow = 4 处具有活动事件,那么它将在第一个查询中不存在,但在第二个查询中出现。另一个例子:quiz_masters 根本没有事件。然后它将出现在第一个查询中,而在第二个查询中不存在。

大多数情况下,这种不便是因为left (outer) 连接的错误使用:在where 子句中使用左连接表将其转换为(inner) 连接。如果left join 工作正常 - 第一个查询将为空,第二个查询将在事件 BTW 上独立返回 quiz_masters 中的所有活动条目。

【讨论】:

以上是关于重构 postgres join vs except的主要内容,如果未能解决你的问题,请参考以下文章

Postgres JOIN 与 unnest

Postgres COUNT 个带有 INNER JOIN 的列值

在 Postgres JOIN 查询中区分 null 和 empty

SQL / Postgres join where子句

如何对具有纪元值的列进行 JOIN 查询,忽略 postgres 中的时间部分

Postgres join on min(*) + group by super slow