加入四个涉及LEFT JOIN的表,没有重复

时间:2015-07-17 18:37:08

标签: sql postgresql join sqlalchemy left-join

我希望使用 null 值连接四个表而不重复,然后将其转换为SQLAlchemy查询。

表格(简化):

Category(id, name)
Task(id, category.id, name)
User(id, name)

还有一个多对多的表格:

Solved(task.id, user.id)

我希望获得包含其类别的所有任务以及具有解决任务的特定用户的列:

+---------------+-----------+-----------+
| category.name | task.name | user.name |
+---------------+-----------+-----------+
| abc           | abctask1  | <null>    |
| abc           | abctask2  | luke      |
| def           | deftask1  | <null>    |
| ghi           | ghitask1  | <null>    |
| ghi           | ghitask2  | luke      |
+---------------+-----------+-----------+

目前我有3到4个单独的SQLAlchemy查询来执行该任务。如果可能的话,它应该只合并到一个查询中,以避免对数据库进行过多的读取。

到目前为止,我已经:

SELECT DISTINCT
  cat.name, t.name, u.name
FROM
  Task t
JOIN 
  Category cat ON cat.id = t.category_id
LEFT JOIN 
  Solved s ON s.task_id = t.id
LEFT JOIN 
  User u ON s.user_id = u.id AND
  u.name = 'luke'
ORDER BY
  cat.name

但是,虽然DISTINCT,我从给定用户的所有行中得到了重复:

+---------------+-----------+-----------+
| category.name | task.name | user.name |
+---------------+-----------+-----------+
| abc           | abctask1  | <null>    |
| abc           | abctask2  | luke      |
| abc           | abctask2  | <null>    | <-- duplicate
| def           | deftask1  | <null>    |
| ghi           | ghitask1  | <null>    |
| ghi           | ghitask2  | luke      |
| ghi           | ghitask2  | <null>    | <-- duplicate
+---------------+-----------+-----------+

是否有可能通过一个查询获取此表并将其转换为SQLAlchemy?

2 个答案:

答案 0 :(得分:1)

问题来自你的数据,即你可能有2个任务叫做abctask2 / ghitask2。也许你应该对taskname设置一个约束。您的查询效果很好。

http://sqlfiddle.com/#!9/c4647c/4

尝试检查

SELECT category_id, name ,count(*) from TASK GROUP BY category_id, name HAVING COUNT(*)<>1

答案 1 :(得分:1)

您有两个LEFT JOINS

  • 第一个左连接可以从solved连接到多个行。说,&#39; jane&#39;和&#39;卢克&#39;解决了这个任务。
  • 第二个左连接只能加入名为&#39; luke&#39; (&#39; luke&#39;在加入条件下!)。

您仍然可以 行,&#39; jane&#39;如果没有显示,连接条件会将她过滤掉,但LEFT JOIN仍保留结果中的行并附加NULL值。

您可以使用括号[INNER] JOIN代替LEFT JOINsolved之间的users来实现您的目标。 The manual:

  

如有必要,请使用括号来确定嵌套顺序。在里面   没有括号,JOIN从左到右的巢。

SELECT c.name AS cat_name, t.name AS task_name, u.name AS user_name
FROM   task t
JOIN   category c ON cat.id = t.category_id
LEFT   JOIN
      (solved s JOIN users u ON u.id = s.user_id AND u.name = 'luke') ON s.task_id = t.id
ORDER  BY 1, 2, 3;
  • 使用表名users代替保留字 user

  • 假设users.name已定义为唯一,或者您可以将多个用户命名为&#39; luke&#39;。

  • 如果(task.id, users.id)中的solved定义为UNIQUEPRIMARY KEY,则根本不需要DISTINCT

结果查询不仅正确,而且速度更快。

以上查询的SqlAlchemy版本:contributed by @van
这假定CategoryTaskUser是映射类,而solvedTable的实例(只是一个关联表,如代码示例{{ 3}}):

user_name = 'luke'
q = (session.query(Category.name, Task.name, User.name)
     .select_from(Task)
     .join(Category)
     .outerjoin(
         join(solved, User,
              (solved.c.user_id == User.id) & (User.name == user_name),
         ))
     .order_by(Category.name, Task.name, User.name)
     )