我有2个多对多的表,还有一个表来加入它们。
官
报告
report_officer
我想选择所有未与报告相关联的人员或在特定时间范围内未与报告关联的人员。
到目前为止,我已尝试过以下内容(下面对我不起作用!):
SELECT *
FROM Officer
LEFT JOIN report_officer
ON Officer.id = report_officer.officer_id
LEFT JOIN Report
ON Report.id = report_officer.report_id
WHERE (performanceDate IS NULL
OR performanceDate < "2014-03-23 00:00:00"
OR performanceDate > "2014-04-01 00:00:00"
)
我的左连接查询仅在官员在特定时间范围内与报告关联时才有效,但在有多个报告后失败。
结果:
+------------+-----------------+
| officer_id | performanceDate |
+------------+-----------------+
| 130 | NULL | # good
| 134 | 2014-03-02 | # bad - officer_id 134 has a performanceDate
| 134 | 2014-03-09 | # on 2014-3-30, I do not want this in the results.
| 134 | 2014-03-16 | #
| 135 | 2014-03-02 | # good
+------------+-----------------+
SQL小提琴: http://sqlfiddle.com/#!2/1bf72/3&lt; - 在sql小提琴中,请参阅我想要返回的列的'name'字段。
关于如何使这项工作的任何想法?
理想情况下,我希望尽可能简单地使用我的ORM。我正在使用doctrine并且不想开始使用完全自定义的代码(所以如果只使用连接可以完成,那就太好了)。我感觉不好,我需要一个子查询。
答案 0 :(得分:4)
SELECT Officer.*, Report.performanceDate FROM Officer
LEFT JOIN report_officer ON Officer.id = report_officer.officer_id
LEFT JOIN Report ON Report.id = report_officer.report_id
AND
(performanceDate > "2014-03-23 00:00:00" AND
performanceDate < "2014-04-01 00:00:00")
WHERE Report.id IS NULL
您只想连接特定日期范围内的行,因此必须将约束移动到连接的on
子句中并反转约束。
如果您想删除重复项,可以尝试group by
:
SELECT Officer.id, MAX(Report.performanceDate) FROM Officer
LEFT JOIN report_officer ON Officer.id = report_officer.officer_id
LEFT JOIN Report ON Report.id = report_officer.report_id
AND
(performanceDate > "2014-03-23 00:00:00" AND
performanceDate < "2014-04-01 00:00:00")
WHERE Report.id IS NULL
GROUP BY Officer.id
但是如果您所请求的日期范围内有多个效果日期(或者您可以使用GROUP_CONCAT
收集所有日期),则必须决定要获得的日期。
实际上我相对确定,LEFT JOIN
根本无法实现你想要实现的目标......
总是有效的是子查询解决方案:
SELECT Officer.id as OfficerID, Officer.name,
Report.id as ReportID,
Report.performanceDate
FROM Officer
LEFT JOIN report_officer
ON Officer.id = report_officer.officer_id
LEFT JOIN Report
ON Report.id = report_officer.report_id
WHERE Report.id IS NULL
OR NOT EXISTS (
SELECT * FROM report_officer
INNER JOIN Report ON report_id = Report.id
WHERE officer_id = Officer.id AND
performanceDate > "2014-03-23 00:00:00"
AND performanceDate < "2014-04-01 00:00:00"
)
但这些并不是那么高效......这个看起来是否有报告禁止输出该行。
答案 1 :(得分:2)
我想选择所有与之无关的军官 报告或未与某个报告相关联的报告 时间表。
你的两个条件是多余的:如果一个官员没有被关联,那么他也不能在任何时间范围内被关联,并且将被第二个条件选中。如果他在时间范围内有报告,那么由于第二个条件他没有被选中,但他也至少有一个报告而且不能满足第一个报告。
所以你想要的是,“一名在时间框架内没有报告的军官”。
要这样做,只需反转条件:首先在选定的时间范围内获取的那些报告(即不想要的那些人员);然后LEFT JOIN
与Officer
,要求连接产生null。这将为您提供其他人员,即在所选时间范围内没有报告的人员(或者根本没有报告)。
但是,在这种情况下,不能有报告日期,因为您没有报告(对于那些根本没有报告的人员来说,这更为明显):
SELECT
Officer.id as OfficerID,
Officer.name,
MAX(Report.id) as ReportID,
MAX(performanceDate) AS performanceDate
FROM Officer
LEFT JOIN report_officer ON (Officer.id = report_officer.officer_id)
LEFT JOIN Report ON (Report.id = report_officer.report_id
AND performanceDate BETWEEN 20140323 AND 20140401)
GROUP BY Officer.id, Officer.name
HAVING ReportID IS NULL;
我不知道Doctrine和HAVING
。如果你不能使用HAVING
子句,你可以尝试通过运行它来模拟它,这应该是非常标准的:
SELECT
Officer.id as OfficerID,
Officer.name,
COUNT(Report.id) as reports
FROM Officer
LEFT JOIN report_officer ON (Officer.id = report_officer.officer_id)
LEFT JOIN Report ON (Report.id = report_officer.report_id
AND performanceDate BETWEEN 20140323000000 AND 20140401235959)
GROUP BY Officer.id, Officer.name;
然后应用reports
等于0的过滤器,即在给定的时间范围内没有报告。您可以添加MAX(performanceDate) AS performanceDate, MAX(Report.id) AS ReportID
以获取在时间范围之外至少有一个的官员的(例如最新的)报告的日期。这可能不是您想要的报告。
指定日期范围时必须小心,因为YYYYMMDD通常等于YYYYMMDD000000,这可能导致相当于半包含范围。否则,请将BETWEEN
替换为performanceDate >= '2014-03-23 00:00:00' AND performanceDate <= '2014-04-01 23:59:59'
。
答案 2 :(得分:1)
感谢大家对此问题的帮助。我的最终解决方案是使用GROUP BY
和HAVING
子句。
@Iserni,我不需要SELECT
在时间范围内有0个报告的官员,我能够SELECT
所有在时间范围之外报告的人员或使用{{ 1}}。
这是我的最终代码:
HAVING
这样做的好处是,我可以利用performanceDate报告上次官员提交报告或报告他从未创建报告的时间。建议的所有其他解决方案都删除了在上次官员创建报告时检索有价值信息的功能。
答案 3 :(得分:0)
添加其他AND
条件可能会解决您的问题。
AND performanceDate NOT BETWEEN "2014-03-23 00:00:00" AND "2014-04-01 00:00:00"
答案 4 :(得分:0)
或者您可以排除满足您条件的记录......
SELECT *
FROM OFFICER
WHERE ID NOT IN (SELECT OFFICER_ID
FROM REPORT_OFFICER)
OR ID NOT IN (SELECT OFFICER_ID
FROM REPORT_OFFICER
WHERE performanceDate BETWEEN "2014-03-23 00:00:00" AND "2014-04-01 00:00:00")
答案 5 :(得分:0)
您可以使用如下所示的WHERE NOT EXISTS语句吗?
SELECT *
FROM Officer
WHERE NOT EXISTS
(
SELECT Report.ID
FROM
Report_Officer
INNER JOIN
Report ON
Report_Officer.Report_ID = Report.ID
WHERE
Report_Officer.Officer_ID = Officer.ID AND
Report.PerformanceDate BETWEEN "2014-03-23 00:00:00" AND "2014-04-01 00:00:00"
)