我有三个表: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绊倒我的方式,但我觉得除此之外应该有办法做到这一点。如果有人有任何建议我会非常感激。感谢。
答案 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
转换空值
答案 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 JOIN
和INNER 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
- 资源表
创建表#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”强>
希望它有所帮助。