相关子查询对你来说很麻烦,特别是在使用EXCEPT时。
换句话说,你只对指定的工人拥有所有工作单元技能的work_unit_id
感兴趣吗? (如果一个工作单元有一个与之关联的技能,但指定的用户没有这个技能,则排除该工作单元?)
这可以通过JOIN和GROUP BY实现,完全不需要相关性。
SELECT
work_units.*
FROM
work_units
INNER JOIN
(
SELECT
wus.work_unit_id
FROM
work_unit_skills wus
LEFT JOIN
workers_skills ws
ON ws.skill_id = wus.skill_id
AND ws.worker_id = 1
GROUP BY
wus.work_unit_id
HAVING
COUNT(wus.skill_id) = COUNT(ws.skill_id)
)
applicable_work_units
ON applicable_work_units.work_unit_id = work_units.id
LIMIT 1
子查询比较工人的技能集与每个工作单元的技能集。如果工作单元具有而工人没有的任何技能,则该行的
ws.skill_id
将为
NULL
,由于
NULL
被
COUNT()
忽略,这意味着
COUNT(ws.skill_id)
将小于
COUNT(wus.skill_id)
,因此该
work_unit
将从子查询的结果中排除。
假设
workers_skills
表在
(work_id, skill_id)
上是唯一的,而
work_unit_skills
表在
(work_unit_id, skill_id)
上是唯一的。如果不是这种情况,则可能需要调整
HAVING
子句(例如:
COUNT(DISTINT wus.skill_id)
等)。
编辑:以上查询假定只有相对较少数量的工作单元与某个特定工人匹配。如果假设相对较大数量的工作单元符合条件,则相反的逻辑更快。(本质上,尝试使子查询返回的行数尽可能少。)
SELECT
work_units.*
FROM
work_units
LEFT JOIN
(
SELECT
wus.work_unit_id
FROM
work_unit_skills wus
LEFT JOIN
workers_skills ws
ON ws.skill_id = wus.skill_id
AND ws.worker_id = 1
WHERE
ws.skill_id IS NULL
GROUP BY
wus.work_unit_id
)
excluded_work_units
ON excluded_work_units.work_unit_id = work_units.id
WHERE
excluded_work_units.work_unit_id IS NULL
LIMIT 1
这个查询会将所有工作单位所具有的技能与员工的技能进行比较,只保留工作单位拥有但员工没有的技能所在的行。
接着,使用GROUP BY
关键字按照工作单位分组,得到需要被排除的工作单位列表。
通过左连接这个列表到现有的结果中,可以指定仅在子查询中未出现该工作单位时才包含它,即通过指定excluded_work_units.work_unit_id IS NULL
来实现。
有用的在线指南将提到anti-join
和anti-semi-join
。
编辑:
一般而言,我不建议使用位掩码。
不是因为它慢,而是因为它违背了规范化。单个字段表示多个数据项是一种常见的SQL代码异味/反模式,因为数据不再是原子性的。(这会导致未来出现问题,特别是当您拥有的技能过多时,它们无法全部适应位掩码所选择的数据类型,或者在管理技能集合的频繁或复杂更改时。)
话虽如此,如果性能仍然是个问题,去规范化往往是一个非常有用的选项。我建议将位掩码存储在单独的表中,以便清楚地表示它们是去规范化/缓存计算结果。然而,一般来说,这样的选择应该是最后的手段,而不是首选反应。
编辑:示例修订,始终包括没有技能的工作单位...
SELECT
work_units.*
FROM
work_units
INNER JOIN
(
SELECT
w.id AS work_unit_id
FROM
work_units w
LEFT JOIN
work_units_skills wus
ON wus.work_unit_id = w.id
LEFT JOIN
workers_skills ws
ON ws.skill_id = wus.skill_id
AND ws.worker_id = 1
GROUP BY
w.id
HAVING
COUNT(wus.skill_id) = COUNT(ws.skill_id)
)
applicable_work_units
ON applicable_work_units.work_unit_id = work_units.id
excluded_work_units
版本的代码
(上面第二个示例查询)应该可以在不需要修改的情况下处理这种特殊情况
(并且是我最初用于实时性能指标试验的版本)。
ORDER BY something complex bunch of conditions
等)。因此,如果您能发布EXPLAIN
,那可能会有所帮助。 - Kaushik Nayakexplain (analyze, verbose, buffers)
**生成的执行计划。请使用格式化文本,不要使用屏幕截图(http://meta.stackoverflow.com/questions/285551/why-may-i-not-upload-images-of-code-on-so- when-asking-a-question / 285557#285557)。如果您不想(或无法)共享表名,请将其上传到http://explain.depesz.com并启用模糊化计划选项(尽管执行计划很少会显示任何机密信息)。 - user330315