[英]LEFT OUTER JOIN PostgreSQL works as INNER JOIN?
以下查询正常工作
SELECT "employee"."id",
"employee"."created",
(SELECT SUM(minutes) FROM jobs_job AS job WHERE job.employee_id = employee.id AND job.job_date BETWEEN '2018-10-21' AND '2018-10-30')
FROM users_employee AS employee
INNER JOIN users_user AS users ON (employee.user_id = users.id)
WHERE NOT ("users"."status" = 'F')
GROUP BY
employee.id
而且我得到正确的数据(对于在JOB表中没有记录的人为NULL
但是,如果我将相同的查询重构为LEFT OUTER JOIN
SELECT "employee"."id",
"employee"."created",
SUM(job.minutes) AS job_minutes
FROM users_employee AS employee
INNER JOIN users_user AS users ON (employee.user_id = users.id)
LEFT OUTER JOIN jobs_job AS job on employee.id = job.employee_id
WHERE NOT ("users"."status" = 'F') AND job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
GROUP BY
employee.id
我在原始查询中得到112行而不是142行,并且仅存在Job表中的记录
这是因为这种咬伤:
AND job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
您需要将其移动到LEFT JOIN,例如:
LEFT OUTER JOIN jobs_job AS job on employee.id = job.employee_id
AND job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
您需要在ON规则中使用其他条件,而不是where子句
SELECT "employee"."id",
"employee"."created",
SUM(job.minutes) AS job_minutes
FROM users_employee AS employee
INNER JOIN users_user AS users ON (employee.user_id = users.id)
LEFT OUTER JOIN jobs_job AS job on employee.id = job.employee_id
and "users"."status" <> 'F' AND job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
GROUP BY
employee.id
您需要将最后一张表上的条件移到on
子句。 第二个表上的过滤保留在where
:
SELECT "employee"."id",
"employee"."created",
SUM(job.minutes) AS job_minutes
FROM users_employee employee INNER JOIN
users_user users
ON employee.user_id = users.id LEFT OUTER JOIN
jobs_job job
ON employee.id = job.employee_id AND
job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
WHERE "users"."status" <> 'F'
GROUP BY employee.id;
该逻辑以这种方式工作,因为左外部联接从JOIN
的第二个表产生的结果为NULL
值。 这些NULL
值可以在WHERE
子句中过滤掉。
提供的所有答案将帮助您修复代码。 我只想扩展一下为什么它们会起作用。
SQL引擎首先评估您的FROM
和JOIN
,然后将您的初始数据集提取到内存中。 到那时,因为您已经使用了LEFT OUTER JOIN
,所以您期望的所有行都仍然存在。
之后,它将应用您的WHERE
子句。 在这种情况下,您的WHERE
子句在job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
包含job.job_date BETWEEN '2018-10-21' AND '2018-10-30'
,因此此时引擎会过滤掉所有不符合该条件的行。 这样可以有效地使您从LEFT JOIN
获得的结果与使用INNER JOIN
获得的结果完全相同。
多次建议的最佳答案是将过滤条件移至ON
子句。 一种可行的替代方法是,将IS NULL
可能性添加到您现有的WHERE
子句中,并且可以证明结果实际上是从头开始的:
...
WHERE
NOT ("users"."status" = 'F')
AND
(
(job.job_date BETWEEN '2018-10-21' AND '2018-10-30')
OR
job.job_date IS NULL
)
但是,使用JOIN
上的条件会更好,因为首先将较少的记录拉入内存。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.