返回GROUP中的前n行使用INNER连接查询来自多个表

时间:2016-06-26 20:47:34

标签: sql-server tsql

我已经尝试了在您的网站上找到的几种解决方案,但没有什么能与我需要的完全匹配。在SQL Server中,我需要使用GROUP BY查询进行多表连接,并仅返回每个组中的TOP 1行。以下是返回所需记录的基本代码,但将返回符合条件的所有记录:

SELECT MIN(Maintenance_Log.Log_StartDate) As MaintDate
     , Manufacturer.Manufacturer_Name
     , GL_Inventory.Inventory_Model
     , GL_Inventory.Inventory_SerialNr
     , Maintenance_Log.Event_ID
     , Maintenance_Log.Inventory_ID 
FROM Maintenance_Log 
INNER JOIN GL_Inventory ON Maintenance_Log.Inventory_ID = GL_Inventory.Inventory_ID 
INNER JOIN Manufacturer ON GL_Inventory.Manufacturer_ID = Manufacturer.Manufacturer_ID  
WHERE Maintenance_Log.Owner_ID = @OwnerID 
  AND Maintenance_Log.Log_CompletedDate Is Null 
GROUP BY Maintenance_Log.Inventory_ID
       , Maintenance_Log.Log_StartDate
       , Manufacturer.Manufacturer_Name
       , GL_Inventory.Inventory_Model
       , GL_Inventory.Inventory_SerialNr
       , Maintenance_Log.Event_ID 

返回的结果是:

2016-06-30 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    2   6
2016-07-28 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    3   6
2016-08-25 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    4   6
2016-09-29 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    5   6
2016-10-27 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    6   6
2016-11-24 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    7   6
2016-12-29 09:00:00.000 GLOCK   G19 Gen 4   ABDE1234    8   6
2016-07-01 09:00:00.000 S&W     642         WQ32De45    9   7
2016-08-05 09:00:00.000 S&W     642         WQ32De45    10  7
2016-09-02 09:00:00.000 S&W     642         WQ32De45    11  7
2016-10-07 09:00:00.000 S&W     642         WQ32De45    12  7
2016-11-04 09:00:00.000 S&W     642         WQ32De45    13  7
2016-12-02 09:00:00.000 S&W     642         WQ32De45    14  7

我想根据行中的最后一位数字{@ 1}}返回每个组中的 TOP 1 行,而不是所有符合条件的行。

4 个答案:

答案 0 :(得分:0)

这是您的查询,使用表别名进行格式化和简化:

SELECT MIN(l.Log_StartDate) As MaintDate, m.Manufacturer_Name, 
       i.Inventory_Model, i.Inventory_SerialNr, l.Event_ID, l.Inventory_ID 
FROM Maintenance_Log l INNER JOIN
     GL_Inventory i
     ON l.Inventory_ID = i.Inventory_ID INNER JOIN
     Manufacturer m
     ON i.Manufacturer_ID = m.Manufacturer_ID)
WHERE l.Owner_ID = @OwnerID AND l.Log_CompletedDate Is Null 
GROUP BY l.Log_StartDate, m.Manufacturer_Name, i.Inventory_Model, 
         i.Inventory_SerialNr, l.Event_ID, l.Inventory_ID ;

首先,我注意到Log_StartDate位于GROUP BYMIN()的参数中。所以,我想知道这是否能解决你的问题:

SELECT MIN(l.Log_StartDate) As MaintDate, m.Manufacturer_Name, 
       i.Inventory_Model, i.Inventory_SerialNr, l.Event_ID, l.Inventory_ID 
FROM Maintenance_Log l INNER JOIN
     GL_Inventory i
     ON l.Inventory_ID = i.Inventory_ID INNER JOIN
     Manufacturer m
     ON i.Manufacturer_ID = m.Manufacturer_ID)
WHERE l.Owner_ID = @OwnerID AND l.Log_CompletedDate Is Null 
GROUP BY m.Manufacturer_Name, i.Inventory_Model, 
         i.Inventory_SerialNr, l.Event_ID, l.Inventory_ID ;

如果您仍然想要最大inventory_id的一行,那么您可以使用窗口函数:

WITH t as (
      SELECT MIN(l.Log_StartDate) As MaintDate, m.Manufacturer_Name, 
             i.Inventory_Model, i.Inventory_SerialNr, l.Event_ID, l.Inventory_ID ,
             ROW_NUMBER() OVER (PARTITION BY m.Manufacturer_Name,  i.Inventory_Model, i.Inventory_SerialNr, l.Event_ID
                                ORDER BY l.Inventory_ID  -- Do you want this ASC or DESC ?
                               ) as seqnum
      FROM Maintenance_Log l INNER JOIN
           GL_Inventory i
           ON l.Inventory_ID = i.Inventory_ID INNER JOIN
           Manufacturer m
           ON i.Manufacturer_ID = m.Manufacturer_ID)
      WHERE l.Owner_ID = @OwnerID AND l.Log_CompletedDate Is Null 
      GROUP BY m.Manufacturer_Name, i.Inventory_Model, 
               i.Inventory_SerialNr, l.Event_ID, l.Inventory_ID
     )
SELECT t.*
FROM t
WHERE seqnum = 1;

答案 1 :(得分:0)

WITH X AS (
SELECT ML.Log_StartDate As MaintDate
     , M.M_Name
     , I.Inventory_Model
     , I.Inventory_SerialNr
     , ML.Event_ID
     , ML.Inventory_ID 
     , ROW_NUMBER() OVER (PARTITION BY ML.Inventory_ID 
                                    ORDER BY ML.Log_StartDate DESC)rn
FROM Maintenance_Log ML 
INNER JOIN GL_Inventory I ON ML.Inventory_ID = I.Inventory_ID 
INNER JOIN Manufacturer M ON I.M_ID = M.M_ID  
WHERE ML.Owner_ID = @OwnerID 
  AND ML.Log_CompletedDate Is Null 
)
SELECT MaintDate
      ,M_Name
      ,Inventory_Model
      ,Inventory_SerialNr
      ,Event_ID
      ,Inventory_ID
FROM X
WHERE RN = 1

答案 2 :(得分:0)

通常在这种情况下(需要从每个要连接的行的子查询得到前1个结果),CROSS APPLY就是你想要的。

INNER JOIN(选择前1 ...)对于要连接的每一行将使用相同的“前1”行,而CROSS APPLY(选择前1 ...)将评估要连接的每一行的表达式。

如果你需要它表现为LEFT OUTER JOIN(不过滤表达式中没有结果的行),请改用OUTER APPLY。

答案 3 :(得分:0)

由于每行的唯一性,查询完成了您的要求。

根据您发布的数据(假设第一个日期将具有最小的Maintenance_Log.Event_ID),下面的查询可能是一个解决方案。

 SELECT MIN(Maintenance_Log.Log_StartDate) As MaintDate
     , Manufacturer.Manufacturer_Name
     , GL_Inventory.Inventory_Model
     , GL_Inventory.Inventory_SerialNr
     , MIN(Maintenance_Log.Event_ID)
     , Maintenance_Log.Inventory_ID 
FROM Maintenance_Log 
INNER JOIN GL_Inventory ON Maintenance_Log.Inventory_ID = GL_Inventory.Inventory_ID 
INNER JOIN Manufacturer ON GL_Inventory.Manufacturer_ID = Manufacturer.Manufacturer_ID  
WHERE Maintenance_Log.Owner_ID = @OwnerID 
  AND Maintenance_Log.Log_CompletedDate Is Null 
GROUP BY Maintenance_Log.Inventory_ID
       , Maintenance_Log.Log_StartDate
       , Manufacturer.Manufacturer_Name
       , GL_Inventory.Inventory_Model
       , GL_Inventory.Inventory_SerialNr
       --, Maintenance_Log.Event_ID