每组每个项目的最后记录

时间:2019-12-16 22:56:24

标签: sql sql-server greatest-n-per-group

简化问题: 在表格“ table1”中,我们具有以下列:RowID,ItemID,BranchID,RoomID,日期,数量

我正在尝试在每个BranchID中的每个RoomID中检索ItemID的最后一个数量。

一旦我知道了,计划就是将table1联接到ItemIDTable,BranchIDTable,RoomIDTable以获取ID的名称。

通过使用MAX(Date),我获得了仅在一个RoomID中的ItemID的数量,但是,如果ItemID在多个RoomID中,则函数MAX(Date)返回所有房间中的最新记录,而我需要每个房间的最新信息。

为ItemID = 50和BranchID = 4设置的数据:

+--------+----------+--------+-----+---------------------+------------+------------+--------------+
| ItemID | BranchID | RoomID | Qty |        Date         | ItemIDName | RoomIDNAme | BranchIDName |
+--------+----------+--------+-----+---------------------+------------+------------+--------------+
|     50 |        4 |      1 |   7 | 2019-12-12 13:30:15 | ItemA      | RoomB      | BranchB      |
|     50 |        4 |      2 |   5 | 2019-12-12 13:30:20 | ItemA      | RoomA      | BranchB      |
|     50 |        4 |      2 |   8 | 2019-12-12 13:30:25 | ItemA      | RoomA      | BranchB      |
+--------+----------+--------+-----+---------------------+------------+------------+--------------+

我得到的结果(它从两个RoomID中选择了最新的一个):

+--------+----------+--------+-----+---------------------+------------+------------+--------------+
| ItemID | BranchID | RoomID | Qty |        Date         | ItemIDName | RoomIDNAme | BranchIDName |
+--------+----------+--------+-----+---------------------+------------+------------+--------------+
|     50 |        4 |      2 |   8 | 2019-12-12 13:30:25 | ItemA      | RoomA      | BranchB      |
+--------+----------+--------+-----+---------------------+------------+------------+--------------+

预期(每个RoomID的最新数量):

+--------+----------+--------+-----+---------------------+------------+------------+--------------+
| ItemID | BranchID | RoomID | Qty |        Date         | ItemIDName | RoomIDNAme | BranchIDName |
+--------+----------+--------+-----+---------------------+------------+------------+--------------+
|     50 |        4 |      1 |   7 | 2019-12-12 13:30:15 | ItemA      | RoomB      | BranchB      |
|     50 |        4 |      2 |   8 | 2019-12-12 13:30:25 | ItemA      | RoomA      | BranchB      |
+--------+----------+--------+-----+---------------------+------------+------------+--------------+

查询本身:

SELECT     
    table1.ItemID, 
    table1.BranchID, 
    table1.RoomID, 
    table1.Qty, 
    table1.Date, 
    ItemIDTable.ItemIDName, 
    RoomIDTable.RoomIDName, 
    BranchIDTable.BranchIDName
FROM         
    table1 INNER JOIN
    ItemIDTable ON table1.ItemID = ItemIDTable.ItemID INNER JOIN
    RoomIDTable ON table1.RoomID = RoomIDTable.RoomID INNER JOIN
    BranchIDTable ON table1.BranchID = BranchIDTable.BranchID
WHERE     
    (table1.Date IN
        (
            SELECT     MAX(Date)
            FROM          table1
            WHERE      (ItemID = table1.ItemID) AND (BranchID = table1.BranchID)
        )
    )
ORDER BY 
    table1.ItemID

我试图缩短和简化标题以使其更具可读性。无论是使此查询正常工作还是使用更好的方法,A都会对此表示赞赏。

2 个答案:

答案 0 :(得分:1)

您可以使用ROW_NUMBER()窗口功能:

DECLARE @Tab TABLE (ItemID INT, BranchID INT, RoomID INT, Qty INT,Dt datetime,ItemIDName varchar(10),RoomIDNAme varchar(10),BranchIDName varchar(10))
insert @tab
values
(50,4,1,7,'2019-12-12 13:30:15','ItemA','RoomB','BranchB'),
(50,4,2,5,'2019-12-12 13:30:20','ItemA','RoomA','BranchB'),
(50,4,2,8,'2019-12-12 13:30:25','ItemA','RoomA','BranchB')

 SELECT *
 FROM(
     SELECT row_number() over(partition by itemid, branchid, roomid order by dt desc) rn, *
     FROM @Tab
     ) t
 WHERE t.rn = 1

或与您的查询:

SELECT *
FROM(
    SELECT ROW_NUMBER() OVER(PARTITION BY table1.ItemID, table1.BranchID, table1.RoomID, ORDER BY table1.Date DESC) rn
        table1.ItemID, 
        table1.BranchID, 
        table1.RoomID, 
        table1.Qty, 
        table1.Date, 
        ItemIDTable.ItemIDName, 
        RoomIDTable.RoomIDName, 
        BranchIDTable.BranchIDName
    FROM         
        table1 INNER JOIN
        ItemIDTable ON table1.ItemID = ItemIDTable.ItemID INNER JOIN
        RoomIDTable ON table1.RoomID = RoomIDTable.RoomID INNER JOIN
        BranchIDTable ON table1.BranchID = BranchIDTable.BranchID
    WHERE     
        (table1.Date IN
            (
                SELECT     MAX(Date)
                FROM          table1
                WHERE      (ItemID = table1.ItemID) AND (BranchID = table1.BranchID)
            )
        )
  ) t
 WHERE t.rn = 1
ORDER BY 
    table1.ItemID

答案 1 :(得分:1)

  

我正在尝试在每个BranchID中的每个RoomID中检索ItemID的最后一个数量。

您的意图是使用相关子查询进行过滤对我来说似乎是个好主意。但是,您的WHERE子句不能完全满足您的要求:它似乎缺少RoomID上的条件:

WHERE  
    (table1.Date IN
        (
            SELECT     MAX(Date)
            FROM          table1
            WHERE      (ItemID = table1.ItemID) AND (BranchID = table1.BranchID)
        )
    )

此外,应该重写条件以使用等于而不是IN,因为子查询无论如何都会返回最大一条记录。请注意,此处大多数括号是多余的。最后,我建议使用表别名来消除子查询中的列名。

考虑:

SELECT     
    t1.ItemID, 
    t1.BranchID, 
    t1.RoomID, 
    t1.Qty, 
    t1.Date, 
    i.ItemIDName, 
    r.RoomIDName, 
    b.BranchIDName
FROM         
    table1 t1
    INNER JOIN ItemIDTable i ON t1.ItemID = i.ItemID
    INNER JOIN RoomIDTable r ON t1.RoomID = r.RoomID
    INNER JOIN BranchIDTable ON t1.BranchID = b.BranchID
WHERE t1.Date = (
    SELECT MAX(t11.Date)
    FROM   table1 t11
    WHERE 
        t11.ItemID = t1.ItemID
        AND t11.BranchID = t1.BranchID
        AND t11.RoomID = t1.RoomID
    )
ORDER BY t1.ItemID