【问题标题】:Join three tables by one foreign key通过一个外键连接三个表
【发布时间】:2020-12-10 21:32:16
【问题描述】:

我有三张桌子:

Task (ID, TaskDescription)
Schedule (TaskID, ID, DueAt)
Audit (TaskID, TestID)

Schedule 表中有一个计划任务列表,Audit 表是已经完成的任务。所以首先在Schedule 中有一行,然后当这个任务完成后,它会从Schedule 表中删除并添加到Audit 表中。

任务表

+----+-----------------+
| ID | TaskDescription |
+----+-----------------+
|  1 | Clean room      |
|  2 | Remove trash    |
+----+-----------------+

时间表

+--------+--------+------------+
|   ID   | TaskID |   DueAt    |
+--------+--------+------------+
| 927847 |      1 | 2020-08-01 |
| 777777 |      2 | 2020-08-07 |
+--------+--------+------------+

审核表

+--------+--------+
| TaskID | TestID |
+--------+--------+
|      1 |      3 |
|      1 |      2 |
|      1 |      1 |
|      2 |      4 |
+--------+--------+

我需要为一个任务 ID 处理所有计划和已完成的任务。例如,我期望的结果:

+---------+-----------------+-------------+----------------+--------+
| Task.ID | TaskDescription | Schedule.ID | Schedule.DueAt | TestID |
+---------+-----------------+-------------+----------------+--------+
|       1 | Clean room      | 927847      | 2020-08-01     | NULL   |
|       1 | Clean room      | NULL        | NULL           | 3      |
|       1 | Clean room      | NULL        | NULL           | 2      |
|       1 | Clean room      | NULL        | NULL           | 1      |
+---------+-----------------+-------------+----------------+--------+

这意味着已经完成了 3 项任务,其中一项计划在 2020 年 8 月 1 日完成。 我尝试了什么:

SELECT
    TaskID = t.ID,
    t.TaskDescription,
    ScheduleID = s.ID,
    ScheduleDueAt = s.DueAt,
    a.TestID
FROM Task t
    LEFT OUTER JOIN Schedule s
        ON (s.TaskID = t.ID)
    LEFT OUTER JOIN Audit a
        ON (a.TaskID = t.ID)
WHERE t.ID = '1'

当然,我得到了错误的结果:

+---------+-----------------+-------------+----------------+--------+
| Task.ID | TaskDescription | Schedule.ID | Schedule.DueAt | TestID |
+---------+-----------------+-------------+----------------+--------+
|       1 | Clean room      |      927847 | 2020-08-01     |      3 |
|       1 | Clean room      |      927847 | 2020-08-01     |      2 |
|       1 | Clean room      |      927847 | 2020-08-01     |      1 |
+---------+-----------------+-------------+----------------+--------+

我将为此使用UNION,但首先想问一下,也许还有更正确的方法。

【问题讨论】:

  • 样本数据和预期结果将帮助我们为您提供帮助。
  • 已经有了预期的结果,但是谢谢,我还添加了额外的样本

标签: sql sql-server select left-join union


【解决方案1】:

您需要 union all scheduleaudit 表并查询 nulls 以查找缺失的列。然后,您可以将该结果与task 表连接起来:

SELECT t.id, t.taskdescription, s.id, s.dueat, s.testid
FROM   task t
JOIN   (SELECT taskid, id, dueat, NULL AS testid
        FROM   schedule
        UNION ALL
        SELECT taskid, NULL, NULL, testid
        FROM audit) s ON t.id = s.taskid

【讨论】:

    【解决方案2】:

    我同意按照@Mureinik 的建议使用UNION ALL 可能是您最好的选择,但只是为了好玩,另一种选择就是这个。

    如果您为每个 taskID 的 TestID 为 0 的审计表添加了另一个条目(在创建新任务时作为默认设置),那么它将允许您加入审计表,而无需联合。

    所以你的审计表应该是这样的:

    +--------+--------+
    | TaskID | TestID |
    +--------+--------+
    |      1 |      0 |
    |      2 |      0 |
    |      1 |      3 |
    |      1 |      2 |
    |      1 |      1 |
    |      2 |      4 |
    +--------+--------+
    

    然后您可以修改查询以正常加入计划表,但仅限于审计表值为 0 的地方。

    最后,为了保持整洁,如果您愿意,可以使用 NULLIF 隐藏该 TestID 的 0:

    Select
        TaskID = t.ID,
        t.TaskDescription,
        ScheduleID = s.ID,
        ScheduleDueAt = s.DueAt,
        TestID= nullIF(a.TestID,0) 
    from
        Task t
        inner join
            Audit a on 
                a.TaskID = t.ID
        left join 
            Schedule s on 
                s.TaskID = t.ID
                and a.TaskID = 0
    where
        t.ID = 1
    

    更新:当没有计划任务时,您还需要一个额外的 where 子句,以防止返回空行:

    where
        t.ID = 1
        and not (s.TaskID is null and a.TestID = 0)
    

    【讨论】:

    • 非常感谢您的努力,但我的问题是我无法修改表格并添加新的列/条目,因为表格是由软件提供的,否则我将重写整个表格架构并只制作两个表 - 任务和审计,审计将有 DueAt 列
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 2020-09-15
    • 2019-08-03
    • 2016-07-04
    • 1970-01-01
    • 2014-10-16
    • 2018-08-17
    相关资源
    最近更新 更多