跨多个连接表的SQL查询

时间:2013-07-18 18:16:09

标签: mysql sql sql-server

我有三个表:Resource,Timesheet,TimePeriod。资源有很多Timesheets,Timesheet有一个TimePeriod。这用于我们跟踪员工时间表。我想弄清楚如何找出本周每个人的时间表的状态。问题是,如果一个人没有填写时间表,那么时间表中就不会有条目。所以他们的状态应该是NULL。

这就是数据库的样子:

Resource
id | Name 
---+------------
 1 | John Smith
 2 | Jason Bourne

Timesheet
id | Status    | Resource_Id |TimePeriod_Id
---+-----------+-------------------------------
 1 | Submitted | 1           | 1
 2 | Created   | 1           | 2
 3 | Submitted | 2           | 1


TimePeriod
id | Week 
---+----------------
 1 | 2013Week1
 2 | 2013Week2
 3 | 2013Week3

如果TimePeriod存储在同一个表中,那么这不会有问题。但由于它在一个单独的表中,我认为我正在进行联接的方式存在问题。我无法弄清楚这项工作的查询。

我试过这个:

SELECT res.id, res.name, ts.status
FROM Resource res
LEFT JOIN Timesheet ts ON ts.resource_id = res.id
LEFT JOIN TimePeriod tp ON ts.timeperiod_id = tp.id 
WHERE tp.week = '2013Week2'

这显然从结果中消除了Jason Bourne,因为他没有时间表

我也试过这个:

SELECT res.id, res.name, ts.status
FROM Resource res
LEFT JOIN Timesheet ts ON ts.resource_id = res.id
LEFT JOIN TimePeriod tp ON ts.timeperiod_id = tp.id AND tp.week = '2013Week2'

返回额外的行和错误的数据。

期望的结果是:

id  name           status    
1   John Smith     Created
2   Jason Bourne   NULL  

我相信我可以通过UNION绊倒我的方式,但我觉得除此之外应该有办法做到这一点。如果有人有任何建议我会非常感激。感谢。

7 个答案:

答案 0 :(得分:3)

您需要将Timesheet和内部联接的组合LEFT JOIN连接到TimePeriod

该语法是

SELECT res.id, res.name, ts.status
FROM Resource res
LEFT JOIN (Timesheet ts 
             INNER JOIN TimePeriod tp 
            ON ts.timeperiod_id = tp.id AND tp.week = '2013Week2')
ON ts.resource_id = res.id

您可能还想COALESCE(ts.status, 'unsubmitted') status转换空值

SQL Fiddle

答案 1 :(得分:2)

另一个:

SELECT r.id, r.Name, s.Status
FROM Resource r
INNER JOIN TimePeriod p ON p.week = '2013week2'
LEFT JOIN Timesheet s ON s.Resource_id = r.id AND s.TimePeriod_id = p.id
;

或者,您可以将INNER JOIN ... ON替换为CROSS JOIN ... WHERE

SELECT r.id, r.Name, s.Status
FROM Resource r
CROSS JOIN TimePeriod p
LEFT JOIN Timesheet s ON s.Resource_id = r.id AND s.TimePeriod_id = p.id
WHERE p.week = '2013week2'
;

虽然必须说MySQL不区分CROSS JOININNER JOIN,但将它们视为彼此的同义词。无论如何,上面的查询是标准的SQL,可以在任何SQL产品中使用。

SQL Fiddle的SQL Server演示:http://sqlfiddle.com/#!3/6a0a1/2

答案 2 :(得分:0)

试试这个。

SELECT res.id, res.name, ts.status
FROM [Resource] res
INNER JOIN Timesheet ts ON ts.resource_id = res.id
LEFT JOIN TimePeriod tp ON ts.timeperiod_id = tp.id 
WHERE tp.week = '2013Week2'

这当然不会在本地创建数据。如果这不起作用,请告诉我,我会再次尝试并在本地重新创建您的结构。

答案 3 :(得分:0)

尝试从资源表加入

SELECT res.id, res.name, ts.status
FROM Resource res
RIGHT JOIN Timesheet ts ON ts.resource_id = res.ID
LEFT JOIN TimePeriod tp on ts.timeperiod_id = tp.id AND tp.week - '2013Week2'

通过这种方式,您应该为所有存在的资源获取存在且不存在的时间表,并且只有给定时间段的时间表。

答案 4 :(得分:0)

看起来这样有效。参考下面发布的SQLfiddle。

select r1.id, r1.name, ts.status
from resource r1, resource r2, timesheet ts
where r1.id = r2.id (+) 
and r2.id = ts.resource_id
and ts.timeperiod_id = (select max(id) from timeperiod)

编辑:使用更明确的连接语法:

SELECT r1.id, r1.name, ts.status
FROM resource r1
LEFT OUTER JOIN resource r2 ON r1.id = r2.id
JOIN timesheet ts ON r2.id = ts.resource_id
WHERE ts.timeperiod_id = :timeperiod   -- you can use whatever timeperiod you want, doesn't have to be the max(id)

Sqlfiddle:http://www.sqlfiddle.com/#!4/9dd71/3

答案 5 :(得分:0)

下面的代码使用您在上面提供的数据创建三个本地临时表。

许多人不知道交叉应用可以像完全外连接一样使用。

因此,我们想要本周的资源条目和时间段条目的叉积。无论是否有人提交了时间表。

下一步是将时间表中的资源ID和时间段上的结果保持连接。这将显示已创建和已提交的条目。

假设更新不会将创建的内容移动到提交的列。添加另一个表,其中1 =已创建且2 =已提交,并且取最大值可列出最高值。

结果如下所示。

John Miner,craftydba,www.craftydba.com

enter image description here

- 资源表
创建表#Resource
(res_id int,res_name varchar(128));

- 添加数据
插入#Resource
价值
(1,'John Smith'),
(2,'Jason Bourne');

- 时间段表
创建表#TimePeriod
(tp_id int,tp_week int);

- 添加数据
插入#TimePeriod

(1,201301),
(2,201302),
(3,201303);

- 时间表表
创建表#TimeSheet
(ts_id int,ts_status varchar(16),res_id int,tp_id int);

- 添加数据
插入#TimeSheet

(1,'已提交',1,1),
(2,'创建',1,2),
(3,'已提交',2,1);

- 无论时间段如何,都需要资源和期间 选择*
来自#Resource r
 交叉申请
 (     从#TimePeriod p1中选择*,其中p1.tp_week = 201302
 )作为ca_Period
 在s.res_id = r.res_id和ca_Period.tp_id = s.tp_id左边加入#TimeSheet s

答案 6 :(得分:-1)

试试这个。它会根据您的数据进行测试。但是你不会获得任何资源的NULL值。

SELECT Resource.id,Resource.Name,TimeSheet.Status FROM Resource,TimeSheet,TimePeriod WHERE Resource.id = TimeSheet.id AND TimeSheet.TimePeriod_id = TimePeriod.id AND TimePeriod.Week =“2013Week1”

希望它有所帮助。