表连接匹配无重复

ban*_*ong 5 join sql-server

我有一种情况,我需要在一个位置匹配资产的到达和离开。问题是资产移动并不总是按时间顺序输入数据库,也没有任何当前的方法将数据库中的到达和离开联系在一起。

注意事项:

  • 每个移动(到达或离开)由具有唯一 ID (moveID) 的单行组成
  • 每行都将具有该项目唯一的项目 ID (itemID)。但是,每个项目在表中可能有多行(移动)。
  • 我想按时间顺序将资产的每个到达与出发相匹配,确保我们只将每个 moveID 与一个或更少的其他移动匹配(例如,我们需要允许到达和离开,仅到达或仅离开)。
  • 应根据位置进行匹配(例如,到达时的位置和货架或位置和子货架与出发时的位置和货架或位置和子货架相匹配)。
  • 这是一个 MS SQL Server

表的结构(类似)如下:

  CREATE TABLE Movements
 (
      MoveID int IDENTITY (1,1),
      ItemID int,
      EventType CHAR,
      moveTime datetime,
      LocID int,
      ShelfID int,
      altShelfID int
)
Run Code Online (Sandbox Code Playgroud)

我已经包含了测试数据:

SET IDENTITY_INSERT movements on

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(1,3,'A','2013-01-05 09:00',1,3,NULL)

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(2,3,'D','2013-01-06 13:00',1,3,NULL)

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(3,3,'A','2013-01-07 09:00',1,3,NULL)

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(4,3,'A','2013-01-15 09:00',1,3,NULL)

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(5,3,'D','2013-01-07 15:00',1,3,NULL)

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(6,3,'A','2013-01-16 09:00',2,NULL,7)

INSERT INTO movements
(MoveID,ItemID,EventType,moveTime,LocID,ShelfID,altShelfID)
VALUES(7,3,'D','2013-01-15 12:00',1,3,NULL)

SET IDENTITY_INSERT movements off
Run Code Online (Sandbox Code Playgroud)

询问

SELECT    CASE WHEN sch_A.itemID IS NULL THEN Sch_D.itemID
                     ELSE Sch_A.itemID
                END AS ID ,
                Sch_A.moveTime AS Arrival_Time ,
                Sch_D.moveTime AS Departure_Time ,
                Sch_A.LocID ,
                Sch_A.ShelfID ,
                Sch_A.AltShelfID ,
                Sch_D.LocID ,
                Sch_D.ShelfID ,
                Sch_D.AltShelfID ,
                Sch_A.MoveID AS ArrivalMoveID ,
                Sch_D.MoveID AS DepartureMoveID
      FROM      Movements Sch_A
                FULL JOIN Movements Sch_D ON Sch_A.itemID = Sch_D.itemID
                                                AND Sch_A.LocID = Sch_D.LocID
                                                AND ISNULL(Sch_A.ShelfID,
                                                          0) = ISNULL(Sch_D.ShelfID,
                                                          0)
                                                AND ISNULL(Sch_A.AltShelfID,
                                                          0) = ISNULL(Sch_D.AltShelfID,
                                                          0)
                                                AND Sch_A.MoveID != Sch_D.MoveID
                                                AND Sch_A.moveTime <= Sch_D.moveTime
      WHERE     ( Sch_A.EventType != 'D'
                  OR Sch_A.EventType IS NULL
                )
                AND ( Sch_D.EventType != 'A'
                      OR Sch_D.EventType IS NULL
                    )
Run Code Online (Sandbox Code Playgroud)

返回的结果多次与同一日程表匹配:

3   2013-01-05 09:00:00.000 2013-01-06 13:00:00.000 1   3   NULL    1   3   NULL    1   2
3   2013-01-05 09:00:00.000 2013-01-07 15:00:00.000 1   3   NULL    1   3   NULL    1   5
3   2013-01-05 09:00:00.000 2013-01-15 12:00:00.000 1   3   NULL    1   3   NULL    1   7
3   2013-01-07 09:00:00.000 2013-01-07 15:00:00.000 1   3   NULL    1   3   NULL    3   5
3   2013-01-07 09:00:00.000 2013-01-15 12:00:00.000 1   3   NULL    1   3   NULL    3   7
3   2013-01-15 09:00:00.000 2013-01-15 12:00:00.000 1   3   NULL    1   3   NULL    4   7
3   2013-01-16 09:00:00.000 NULL    2   NULL    7   NULL    NULL    NULL    6   NULL
Run Code Online (Sandbox Code Playgroud)

我想看到的是以下内容:

3   2013-01-05 09:00:00.000 2013-01-06 13:00:00.000 1   3   NULL    1   3   NULL    1   2
3   2013-01-07 09:00:00.000 2013-01-07 15:00:00.000 1   3   NULL    1   3   NULL    3   5
3   2013-01-15 09:00:00.000 2013-01-15 12:00:00.000 1   3   NULL    1   3   NULL    4   7
3   2013-01-16 09:00:00.000 NULL    2   NULL    7   NULL    NULL    NULL    6   NULL
Run Code Online (Sandbox Code Playgroud)

如何减少日程表的重复匹配?谢谢。

更新:正如保罗在下面建议的那样,我可以尝试制作一个相关列来即时匹配项目。执行以下操作有什么危险:

SELECT    CASE WHEN sch_A.itemID IS NULL THEN Sch_D.itemID
                 ELSE Sch_A.itemID
            END AS ID ,
            Sch_A.moveTime AS Arrival_Time ,
            Sch_D.moveTime AS Departure_Time ,
            Sch_A.LocID ,
            Sch_A.ShelfID ,
            Sch_A.AltShelfID ,
            Sch_D.LocID ,
            Sch_D.ShelfID ,
            Sch_D.AltShelfID ,
            Sch_A.MoveID AS ArrivalMoveID ,
            Sch_D.MoveID AS DepartureMoveID
  FROM      Movements Sch_A
            FULL JOIN Movements Sch_D 
                ON Sch_A.itemID = Sch_D.itemID
                    AND Sch_D.MoveID = (select top 1 MoveID from movements i 
                            where i.EventType = 'D' and Sch_A.movetime < i.movetime
                             AND Sch_A.itemID = i.itemID
                                            AND Sch_A.LocID = i.LocID
                                            AND ISNULL(Sch_A.ShelfID,
                                                      0) = ISNULL(i.ShelfID,
                                                      0)
                                            AND ISNULL(Sch_A.AltShelfID,
                                                      0) = ISNULL(i.AltShelfID,
                                                      0)
                                            AND Sch_A.MoveID != i.MoveID        
                                order by i.moveTime ASC)

  WHERE     ( Sch_A.EventType != 'D'
              OR Sch_A.EventType IS NULL
            )
            AND ( Sch_D.EventType != 'A'
                  OR Sch_D.EventType IS NULL
                )
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 1

有点难看,但我只能自己预先计算才能做到这一点。

update movements 
set linkTime = (select top 1 movetime from movements i 
        where i.EventType = 'D' and movements.movetime < i.movetime
        and movements.locid = i.locid
        order by i.moveTime asc)
where movements.EventType = 'A'

select a.*, d.*
from (select * from movements
        where EventType = 'A') as a
left outer join (select * from movements
        where EventType = 'D') as d ON a.locid = d.locid AND a.linktime = d.movetime
Run Code Online (Sandbox Code Playgroud)

本质上,我预先计算了您在 UPDATE 语句中讨论的链接,然后使用它在 SELECT 中根据需要进行链接。我错过了一些与示例数据无关的列(itemID 等)。

插入到 #temporary 表中可能比永久向运动添加新列更有意义,但这将取决于本地知识..

注意:linkTime 是添加到移动表中的日期时间类型的新列