SQL左连接(多重连接条件)

9
我有两个派生表,一个名为“签到”(Check Ins),另一个名为“签退”(Check Outs)。 签到
CheckDate  CheckIn
---------- ---------
08/02/2011 10:10:03
08/02/2011 15:57:16
07/19/2011 13:58:52
07/19/2011 16:50:55
07/26/2011 15:11:24
06/21/2011 12:36:47
08/16/2011 14:49:36
08/09/2011 13:52:10
08/09/2011 16:54:51
08/23/2011 15:48:58
09/06/2011 15:23:00
09/13/2011 10:09:27
09/13/2011 10:40:14
09/13/2011 11:43:14
09/13/2011 11:59:32
09/13/2011 17:05:24
09/20/2011 11:03:42
09/20/2011 12:08:50
09/20/2011 15:21:06
09/20/2011 15:34:29
09/27/2011 11:34:06
10/04/2011 11:37:59
10/04/2011 15:24:04
10/04/2011 16:57:44
10/11/2011 18:19:33

结账(Check Outs)
CheckDate  CheckOut
---------- ---------
08/02/2011 13:29:40
08/02/2011 17:02:25
07/12/2011 17:06:06
07/19/2011 16:40:15
07/19/2011 17:07:35
07/26/2011 14:48:10
07/26/2011 17:27:08
05/31/2011 17:01:39
06/07/2011 17:04:29
06/14/2011 17:08:50
06/21/2011 17:03:46
06/28/2011 17:10:45
07/05/2011 17:02:48
08/16/2011 13:37:36
08/16/2011 17:06:34
08/09/2011 12:00:29
08/09/2011 13:29:36
08/09/2011 14:36:09
08/09/2011 17:00:38
08/23/2011 13:37:11
08/23/2011 17:01:37
09/06/2011 17:00:09
09/13/2011 10:11:50
09/13/2011 11:22:02
09/13/2011 11:47:35
09/13/2011 14:13:36
09/13/2011 14:14:25
09/13/2011 17:08:43
09/20/2011 09:54:55
09/20/2011 11:55:31
09/20/2011 11:55:36
09/20/2011 13:35:16
09/20/2011 15:26:02
09/20/2011 16:33:21
09/20/2011 17:07:52
09/27/2011 11:12:38
10/04/2011 13:26:31
10/04/2011 16:32:56
10/04/2011 17:02:35
10/11/2011 18:25:32

我想将签到和签退匹配起来,条件是两个日志都在同一天,并且签退时间不能早于签到时间,所以我想出了这个查询。
  SELECT A.ChkDt
      AS CheckDate,
         B.CheckIn,
         MIN(A.ChkTm)
      AS CheckOut
    FROM #tempAttLogs
      AS A LEFT JOIN
         (SELECT ChkDt
              AS CheckDate,
                 MIN(ChkTm)
              AS CheckIn
            FROM #tempAttLogs
           WHERE ChkTp = 'I'
        GROUP BY ChkDt) B
      ON A.ChkDt = B.CheckDate
   WHERE ChkTp = 'O' AND
         A.ChkTm > B.CheckIn
GROUP BY A.ChkDt, B.CheckIn

的结果集

CheckDate  CheckIn   CheckOut
---------- --------- ---------
06/21/2011 12:36:47  17:03:46
07/19/2011 13:58:52  16:40:15
07/26/2011 15:11:24  17:27:08
08/02/2011 10:10:03  13:29:40
08/09/2011 13:52:10  14:36:09
08/16/2011 14:49:36  17:06:34
08/23/2011 15:48:58  17:01:37
09/06/2011 15:23:00  17:00:09
09/13/2011 10:09:27  10:11:50
09/20/2011 11:03:42  11:55:31
10/04/2011 11:37:59  13:26:31
10/11/2011 18:19:33  18:25:32

问题是我也想包含没有对应配对的日志。这怎么可能实现呢?
编辑:
我的预期结果集必须包括像这样的日志。
CheckDate  CheckIn   CheckOut
---------- --------- ---------
05/23/2011 NULL      17:04:27

尝试理解逻辑:我期望能够将“in = 2011-02-08T15:57:16”与“out = 2011-02-08T17:02:25”配对,但这似乎没有出现在你的结果集中。它应该出现吗?如果您发布您的预期结果集,那将会很有帮助。 - onedaywhen
你是否总是在同一天签出和签入?如果签出时间接近午夜,那么签入时间会是第二天吗? - Paul Morgan
使用日期时间(datetime)而不是分开的日期和时间可能会简化您的查询。 - Andrew Lazarus
3个回答

8

您可以将任何引用右表的谓词移动到ON子句中,而不是WHERE子句中,以避免将JOIN转换为INNER JOIN

例如:

ON A.ChkDt = B.CheckDate AND
         A.ChkTm > B.CheckIn
WHERE ChkTp = 'O'

不要再使用

    ON A.ChkDt = B.CheckDate
   WHERE ChkTp = 'O' AND
         A.ChkTm > B.CheckIn

从来没想过会这么简单。谢谢! - ELM

2

在您的WHERE子句中包含null值。例如:

WHERE( ChkTp = 'O' AND
         A.ChkTm > B.CheckIn) OR B.CheckDate IS NULL

请注意,在原始问题中,JOIN ... ON A.ChkDt = B.CheckDate WHERE ... A.ChkTm > B.CheckIn 这两个条件有些相互抵消的意思,所以你可能想要使用 A.ChkTm >= B.CheckIn 来代替。 - Seph

0

这可能对解决您的问题有所帮助(未经测试),尽管它与您的问题略有偏差。

select 
  Checkdate, CheckIn as CheckTime, 'I' as CheckAction
from CheckIns 

union all

select 
  Checkdate, CheckOut as CheckTime, 'O' as CheckAction
from CheckOuts
order by 1,2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接