【问题标题】:Join four tables involving LEFT JOIN without duplicates连接四个涉及 LEFT JOIN 的表,不重复
【发布时间】:2015-10-07 13:41:25
【问题描述】:

我想连接四个具有 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?

【问题讨论】:

  • 记录一下:DISTINCT 只会合并完全相同的行。它不会像 OP 中显示的那样删除“几乎重复的”。
  • 显示的结果没有重复。似乎您实际上希望结果中有不同的 tasks (每个任务恰好一行) - 仅当 s(he) 解决了给定用户时?顺便说一句,User 不是没有双引号的有效表名(保留字)。

标签: sql postgresql join sqlalchemy left-join


【解决方案1】:

你有两个LEFT JOINS

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

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

您可以通过使用括号[INNER] JOIN而不是solvedusers之间的LEFT JOIN来实现您的目标。 The manual:

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

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 被定义为唯一,或者您可以拥有多个名为“luke”的用户。

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

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


上述查询的 SqlAlchemy 版本: (contributed by @van)
这假定CategoryTaskUser 是映射类,而solvedTable 的实例(只是代码示例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)
     )

【讨论】:

  • 很好的解释和完美的解决方案,谢谢。
【解决方案2】:

问题来自您的数据,即您可能有 2 个任务称为 abctask2/ghitask2。也许您应该对任务名称进行限制。您的查询效果很好。

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

尝试检查

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

【讨论】:

  • 数据中有解释,不重复。
猜你喜欢
  • 2013-09-28
  • 1970-01-01
  • 2014-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-15
  • 2015-04-25
相关资源
最近更新 更多