无重复地连接涉及 LEFT JOIN 的四个表

Join four tables involving LEFT JOIN without duplicates

我想加入四个 table 具有 null 值且不重复,然后将其转换为 SQLAlchemy 查询。

table是(简体):

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

和多对多table:

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
+---------------+-----------+-----------+

是否有可能通过一次查询获得此 table 并将其转换为 SQLAlchemy?

问题来自您的数据,即您可能有 2 个名为 abctask2/ghitask2 的任务。也许您应该对任务名称施加约束。您的查询很好。

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

尝试检查

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

你有两个LEFT JOINS:

  • 第一个左连接可以连接到 solved 中的多行。比如说,'jane' 和 'luke' 解决了任务。
  • 左边第二个加入只能加入名为'luke'的用户(加入条件为'luke'!)。

你仍然得到 both 行,'jane' 只是没有显示,连接条件过滤掉了她,但是LEFT JOIN 无论如何都会保留结果中的行并附加 NULL 值。

您可以通过使用 括号 [INNER] JOIN 而不是 solved 和 [=18 之间的 LEFT JOIN 来实现您想要的=]. The manual:

Use parentheses if necessary to determine the order of nesting. In the absence of parentheses, JOINs nest left-to-right.

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;
  • 使用table名称users代替保留字user.

  • 假设 users.name 被定义为 unique 或者您可以有多个名为 'luke'.

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

生成的查询不仅正确,而且速度更快。


上述查询的SqlAlchemy版本: (contributed by @van)
这假设 CategoryTaskUser 被映射 类,而 solvedTable 的实例(只是一个关联 table 如代码示例所示 Many to Many):

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)
     )