3

我有三个表:Resource、Timesheet、TimePeriod。一个资源有许多时间表,而一个时间表有一个 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 偶然发现这一点,但我觉得应该有其他方法可以做到这一点。如果有人有任何建议,我将不胜感激。谢谢。

4

7 回答 7

3

您需要 LEFT JOIN 的 Timesheet 组合和 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小提琴

于 2013-07-18T18:40:59.267 回答
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 ... ONCROSS 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

于 2013-07-18T19:04:37.997 回答
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'

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

于 2013-07-18T18:22:44.713 回答
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'

这样,您应该获得所有存在的资源的现有和不存在的时间表,并且只有给定时间段的时间表。

于 2013-07-18T18:23:00.300 回答
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

于 2013-07-18T18:25:24.410 回答
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, 'Submitted', 1 , 1),
(2, 'Created', 1 , 2),
(3, 'Submitted', 2 , 1);

-- 无论时间段如何,都需要资源和周期
select *
from #Resource r
cross apply
(select * from #TimePeriod p1 where p1.tp_week = 201302
) as ca_Period
left join #TimeSheet s on s.res_id = r.res_id and ca_Period .tp_id=s.tp_id

于 2013-07-18T19:35:53.850 回答
-1

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

从 Resource、TimeSheet、TimePeriod 中选择 Resource.id、Resource.Name、TimeSheet.Status,其中 Resource.id = TimeSheet.id AND TimeSheet.TimePeriod_id = TimePeriod.id AND TimePeriod.Week="2013Week1"

希望能帮助到你。

于 2013-07-18T18:29:53.200 回答