如何在涉及NULLS时使用第三个表连接两个表

时间:2017-03-27 23:29:11

标签: sql-server tsql left-join outer-join jointable

我有两张时间卡表,我需要加入。这两个表应该由周ID和员工资源代码(如果适用)连接。但是,除了一周之外,两个表包含的数据来自不同的时间范围(即在大多数情况下,两个表中都没有匹配的数据)。

第一个表格(dt5)包含该周的ID,员工的资源代码,该员工当周的工作能力以及该周的实际工作时数。

DT5:

+---------------+---------------+----------+---------------+
| id            | Resource_code | capacity | time_reported |
+---------------+---------------+----------+---------------+
|             1 |           555 |       40 |            40 |
|             1 |           333 |       25 |            20 |
|             2 |           555 |       40 |            40 |
|             2 |           333 |       25 |            20 |
|             3 |           555 |       40 |            40 |
|             3 |           333 |       25 |            20 |
|             4 |           555 |       40 |            39 |
|             4 |           333 |       25 |            24 |
+---------------+---------------+----------+---------------+

第二个表格(dt4)包含周的ID,员工的资源代码以及该周员工的计划工时。

DT4:

+---------------+---------------+---------------+
| id            | Resource_code | planned_hours |
+---------------+---------------+---------------+
|             4 |           555 |            30 |
|             4 |           333 |            20 | 
|             5 |           555 |            30 |
|             5 |           333 |            20 |
|             6 |           555 |            30 |
|             6 |           333 |            20 | 
+---------------+---------------+---------------+ 

当员工填写时间卡时,计划的小时数据将被删除;在此之前,数据重叠的时间很短(当两个表都有相同时间段的数据时,例如我的示例表中的时间段4)。因为这两个表在任何给定时间只有一个共同的句点,所以我使用的是包含每周ID的第三个表(gtd)来帮助加入它们。

GTD:

+----+------------+----------+
| id | start_date | end_date |
+----+------------+----------+
|  1 |         10 |       20 |
|  2 |         30 |       40 |
|  3 |         50 |       60 |
|  4 |         70 |       80 |
|  5 |         90 |      100 |
|  6 |        110 |      120 |
|  7 |        130 |      140 |
|  8 |        150 |      160 |
|  9 |        170 |      180 |
| 10 |        190 |      200 |
+----+------------+----------+
    为了简化,
  • 日期在此示例中更改为整数

我的结果应如下所示:

请注意,第4周包含来自dt4和dt5的数据(容量,报告的时间,计划的小时数),因为第4周是唯一重叠的一周。

+----+---------------+----------+---------------+---------------+---------------+
| id | Resource_code | capacity | time_reported | Resource_code | planned_hours |
+----+---------------+----------+---------------+---------------+---------------+
|  1 | 555           | 40       | 40            | NULL          | NULL          |
|  1 | 333           | 25       | 20            | NULL          | NULL          |
|  2 | 555           | 40       | 40            | NULL          | NULL          |
|  2 | 333           | 25       | 20            | NULL          | NULL          |
|  3 | 555           | 40       | 40            | NULL          | NULL          |
|  3 | 333           | 25       | 20            | NULL          | NULL          |
|  4 | 555           | 40       | 39            | 555           | 30            |
|  4 | 333           | 25       | 24            | 333           | 20            |
|  5 | NULL          | NULL     | NULL          | 555           | 30            |
|  5 | NULL          | NULL     | NULL          | 333           | 20            |
|  6 | NULL          | NULL     | NULL          | 555           | 30            |
|  6 | NULL          | NULL     | NULL          | 333           | 20            |
|  7 | NULL          | NULL     | NULL          | NULL          | NULL          |
|  8 | NULL          | NULL     | NULL          | NULL          | NULL          |
|  9 | NULL          | NULL     | NULL          | NULL          | NULL          |
| 10 | NULL          | NULL     | NULL          | NULL          | NULL          |
+----+---------------+----------+---------------+---------------+---------------+

这是我到目前为止的SQL:

SELECT 
  gtd.id,   
  dt5.resource_code,    
  dt5.capacity, 
  dt5.time_reported,    
  dt4.resource_code,    
  dt4.planned_hours
FROM gtd
  LEFT JOIN dt5 ON gtd.id = dt5.id
  LEFT OUTER JOIN dt4 ON gtd.id = dt4.id

我的(不正确)结果显示如下:

错误发生在第4周。在第4周的两行中,来自dt4的资源代码和计划小时信息与来自dt5的资源代码不匹配。

+----+---------------+----------+---------------+---------------+---------------+
| id | resource_code | capacity | time_reported | resource_code | planned_hours |
+----+---------------+----------+---------------+---------------+---------------+
|  1 | 555           | 40       | 40            | NULL          | NULL          |
|  1 | 333           | 25       | 20            | NULL          | NULL          |
|  2 | 555           | 40       | 40            | NULL          | NULL          |
|  2 | 333           | 25       | 20            | NULL          | NULL          |
|  3 | 555           | 40       | 40            | NULL          | NULL          |
|  3 | 333           | 25       | 20            | NULL          | NULL          |
|  4 | 555           | 40       | 39            | 555 (Correct) | 30            |
|  4 | 555           | 40       | 39            | 333 (Wrong)   | 20            |
|  4 | 333           | 25       | 24            | 555 (Wrong)   | 30            |
|  4 | 333           | 25       | 24            | 333 (Correct) | 20            |
|  5 | NULL          | NULL     | NULL          | 555           | 30            |
|  5 | NULL          | NULL     | NULL          | 333           | 20            |
|  6 | NULL          | NULL     | NULL          | 555           | 30            |
|  6 | NULL          | NULL     | NULL          | 333           | 20            |
|  7 | NULL          | NULL     | NULL          | NULL          | NULL          |
|  8 | NULL          | NULL     | NULL          | NULL          | NULL          |
|  9 | NULL          | NULL     | NULL          | NULL          | NULL          |
| 10 | NULL          | NULL     | NULL          | NULL          | NULL          |
+----+---------------+----------+---------------+---------------+---------------+

根据我的研究,我认为我要么错误地使用JOINS,要么我需要在某处使用CASE语句。我也试过加入资源代码表,但这消除了我的很多数据。任何正确方向的解决方案或指针都将非常受欢迎。

我正在使用tsql。

*编辑我的问题以修复与列名称的不一致(period_number更改为id)

2 个答案:

答案 0 :(得分:0)

毫无疑问,我的回答是一个更简单,更优雅的解决方案,但由于我在这里非常疲惫,这是一种蛮力方法:

使用UNION将两个表格混合在一起。您需要制作仅存在于一个表格中的虚拟信息(例如Capacity)。

使用组合表并使用GROUP BY

组织数据
SELECT f1.Period, f1.RC, f1.PlanTime, f1.ActTime
FROM
(SELECT  
  dt5.period_number AS 'Period',
  dt5.resource_code AS 'RC',    
  dt5.capacity AS 'ActCap', 
  0 AS 'PlanTime',
  dt5.time_reported AS 'ActTime'
FROM dt5
UNION ALL
SELECT  
  dt4.period_number AS 'Period',
  dt4.resource_code AS 'RC',    
  0 AS 'ActCap', 
  dt4.planned_hours AS 'PlanTime',
  0 AS 'ActTime'
FROM dt4) AS f1
GROUP BY f1.Period, f1.RC

答案 1 :(得分:0)

我认为你不需要gtd表。请试着看看这对你有用吗。如果我对您的请求的理解不正确,请纠正我。

SELECT COALESCE(dt5.period_number, dt4.period_number) AS period_number,
    dt5.Resource_code,
    dt5.capacity,
    dt5.time_reported,
    dt4.Resource_code,
    dt4.planned_hours
FROM dt5
FULL OUTER JOIN (
    SELECT *
    FROM dt4 a
    WHERE NOT EXISTS (
            SELECT 1
            FROM dt5 b
            WHERE b.period_number = a.period_number
                AND b.Resource_code = a.Resource_code
            )
    ) dt4
    ON dt5.period_number = dt4.period_number
        AND dt4.Resource_code = dt5.Resource_code
ORDER BY COALESCE(dt5.period_number, dt4.period_number) ASC

测试数据

;WITH cte_dt5(period_number,Resource_code,capacity,time_reported) AS 
(
SELECT 1, 555, 40, 40 UNION ALL
SELECT 1, 333, 25, 20 UNION ALL
SELECT 2, 555, 40, 40 UNION ALL
SELECT 2, 333, 25, 20 UNION ALL
SELECT 3, 555, 40, 40 UNION ALL
SELECT 3, 333, 25, 20 UNION ALL
SELECT 4, 555, 40, 39 UNION ALL
SELECT 4, 333, 25, 24
)
,cte_dt4 (period_number, Resource_code, planned_hours) AS
(
SELECT 4, 555, 30 UNION ALL
SELECT 4, 333, 20 UNION ALL
SELECT 5, 555, 30 UNION ALL
SELECT 5, 333, 20 UNION ALL
SELECT 6, 555, 30 UNION ALL
SELECT 6, 333, 20
)
SELECT COALESCE(dt5.period_number, dt4.period_number) AS period_number,
    dt5.Resource_code,
    dt5.capacity,
    dt5.time_reported,
    dt4.Resource_code,
    dt4.planned_hours
FROM cte_dt5 AS dt5
FULL OUTER JOIN (
    SELECT *
    FROM cte_dt4 a
    WHERE NOT EXISTS (
            SELECT 1
            FROM cte_dt5 b
            WHERE b.period_number = a.period_number
                AND b.Resource_code = a.Resource_code
            )
    ) dt4
    ON dt5.period_number = dt4.period_number
        AND dt4.Resource_code = dt5.Resource_code
ORDER BY COALESCE(dt5.period_number, dt4.period_number) ASC

结果

+---------------------------------------------------------------------------------+
|period_number|Resource_code|capacity   |time_reported|Resource_code|planned_hours|
+-------------|-------------|-----------|-------------|-------------|-------------+
|1            |555          |40         |40           |NULL         |NULL         |
|1            |333          |25         |20           |NULL         |NULL         |
|2            |555          |40         |40           |NULL         |NULL         |
|2            |333          |25         |20           |NULL         |NULL         |
|3            |555          |40         |40           |NULL         |NULL         |
|3            |333          |25         |20           |NULL         |NULL         |
|4            |555          |40         |39           |NULL         |NULL         |
|4            |333          |25         |24           |NULL         |NULL         |
|5            |NULL         |NULL       |NULL         |333          |20           |
|5            |NULL         |NULL       |NULL         |555          |30           |
|6            |NULL         |NULL       |NULL         |333          |20           |
|6            |NULL         |NULL       |NULL         |555          |30           |
+---------------------------------------------------------------------------------+

根据OP的请求更改代码如下。评论Exist子句将给出所需的结果。

  

user7571220:谢谢你的帮助!一切都是正确的,除了   第4周的计划时间和资源代码(来自dt4)   我试图在他们的那一周包括来自两个表的数据   重叠(第4周)。我基本上试图获得第4周的数据   看起来像我在下面发布的评论。

     

| 4 | 555 | 40 | 39 | 555 | 30 |

     

| 4 | 333 | 25 | 24 | 333 | 20 |

SELECT COALESCE(dt5.period_number, dt4.period_number) AS period_number,
        dt5.Resource_code,
        dt5.capacity,
        dt5.time_reported,
        dt4.Resource_code,
        dt4.planned_hours
    FROM cte_dt5 AS dt5
    FULL OUTER JOIN (
        SELECT *
        FROM cte_dt4 a
        --WHERE NOT EXISTS (
        --        SELECT 1
        --        FROM cte_dt5 b
        --        WHERE b.period_number = a.period_number
        --            AND b.Resource_code = a.Resource_code
        --        )
        ) dt4
        ON dt5.period_number = dt4.period_number
            AND dt4.Resource_code = dt5.Resource_code
    ORDER BY COALESCE(dt5.period_number, dt4.period_number) ASC

结果

+---------------------------------------------------------------------------------+
|period_number|Resource_code|capacity   |time_reported|Resource_code|planned_hours|
+-------------|-------------|-----------|-------------|-------------|-------------+
|1            |555          |40         |40           |NULL         |NULL         |
|1            |333          |25         |20           |NULL         |NULL         |
|2            |555          |40         |40           |NULL         |NULL         |
|2            |333          |25         |20           |NULL         |NULL         |
|3            |555          |40         |40           |NULL         |NULL         |
|3            |333          |25         |20           |NULL         |NULL         |
|4            |555          |40         |39           |555          |30           |
|4            |333          |25         |24           |333          |20           |
|5            |NULL         |NULL       |NULL         |333          |20           |
|5            |NULL         |NULL       |NULL         |555          |30           |
|6            |NULL         |NULL       |NULL         |333          |20           |
|6            |NULL         |NULL       |NULL         |555          |30           |
+---------------------------------------------------------------------------------+