如何在PostgreSQL中有效地设置减去连接表?

ndn*_*kov 19 sql postgresql performance set-operations relational-division

我有以下表格:

  • work_units - 自我解释
  • workers - 自我解释
  • skills - 如果您想要工作,每个工作单位都需要一些技能.每个工人都精通各种技能.
  • work_units_skills - 加入表
  • workers_skills - 加入表

工作人员可以请求下一个适当的免费最高优先级(无论这意味着)分配给她的工作单元.


目前我有:

SELECT work_units.*
FROM work_units
-- some joins
WHERE NOT EXISTS (
        SELECT skill_id
        FROM work_units_skills
        WHERE work_unit_id = work_units.id

        EXCEPT

        SELECT skill_id
        FROM workers_skills
        WHERE worker_id = 1 -- the worker id that made the request
      )
-- AND a bunch of other conditions
-- ORDER BY something complex
LIMIT 1
FOR UPDATE SKIP LOCKED;
Run Code Online (Sandbox Code Playgroud)

这种情况使查询慢了8-10倍.

是否有更好的方式来表达work_units技能应该是技能的一部分workers或改善当前查询的东西?


更多背景:

  • skills表是相当小的.
  • 双方work_unitsworkers往往有极少数的相关技能.
  • work_units_skills有索引work_unit_id.
  • 我尝试将查询workers_skills移到CTE中.这略有改善(10-15%),但仍然太慢.
  • 任何用户都可以选择没有技能的工作单位.Aka空集是每一组的子集.

Ste*_*ers 9

一个简单的加速将是使用EXCEPT ALL而不是EXCEPT.后者删除重复项,这在这里是不必要的,可能很慢.

可能更快的替代方案是使用另一个NOT EXISTS而不是EXCEPT:

...
WHERE NOT EXISTS (
        SELECT skill_id
        FROM work_units_skills wus
        WHERE work_unit_id = work_units.id
        AND NOT EXISTS (
            SELECT skill_id
            FROM workers_skills ws
            WHERE worker_id = 1 -- the worker id that made the request
              AND ws.skill_id = wus.skill_id
        )
      )
Run Code Online (Sandbox Code Playgroud)

演示

http://rextester.com/AGEIS52439 - LIMIT删除后进行测试


Dan*_*etz 5

(见下面的更新)

此查询work_unit使用简单的LEFT JOIN 找到一个好处,以在请求工作者具有的较短技能表中找到缺少的技能.诀窍是,每当有一个缺失的技能,也将在加入一个NULL值,这个值转换为1work_unit被留下的所有的人去除0值即具有max0.

作为经典SQL,这将是引擎优化的最有针对性的查询:

SELECT work_unit_id
FROM
  work_units_skills s
LEFT JOIN
  (SELECT skill_id FROM workers_skills WHERE worker_id = 1) t
ON (s.skill_id=t.skill_id)
GROUP BY work_unit_id
HAVING max(CASE WHEN t.skill_id IS NULL THEN 1 ELSE 0 END)=0
-- AND a bunch of other conditions
-- ORDER BY something complex
LIMIT 1
FOR UPDATE SKIP LOCKED;
Run Code Online (Sandbox Code Playgroud)

UPDATE

为了work_units没有技能,我们将work_units表格放入JOIN:

SELECT r.id AS work_unit_id
FROM
  work_units r
LEFT JOIN
  work_units_skills s ON (r.id=s.work_unit_id)
LEFT JOIN
  (SELECT skill_id FROM workers_skills WHERE worker_id = 1) t
ON (s.skill_id=t.skill_id)
GROUP BY r.id
HAVING bool_or(s.skill_id IS NULL) OR bool_and(t.skill_id IS NOT NULL)
-- AND a bunch of other conditions
-- ORDER BY something complex
LIMIT 1
FOR UPDATE SKIP LOCKED;
Run Code Online (Sandbox Code Playgroud)


Tho*_*mas 1

根据目前的信息,我只能凭直觉回答。尝试删除 EXCEPT 语句,看看它是否会变得更快。如果是,您可以再次添加该部分,但使用 WHERE 条件。根据我的经验,集合运算符(减/除、并、交)是性能杀手。