【问题标题】:How to return only the max row from a table in a left join如何仅返回左连接中表的最大行
【发布时间】:2015-06-28 01:45:34
【问题描述】:

我有一张包含汽车信息的表格dbo.Car

CarID | Make      | Model
23      Volvo       XC90
40      Chevrolet   Camaro
46      BMW         Z4   

还有另一个表格dbo.CIS,其中列出了其他信息。它是这样的:

CarID | Dist | U1 | U2 | U3 | U4 | U5 -- U22
23      180    65   94   80   56   88    71
23      1170   68   93   24   23   19    93
40      908    89   15   68   46   59    31
46      500    10   20   30   40   50    35
46      2300   60   62   44   52   75    99

其他表中数据的含义并不重要。但是我想加入CarID,并且当 CarID 在该表中多次出现时,仅返回 Dist 列中具有最高值的 1 行来自 dbo.CIS 表。

所以对于 CarID 23,我想返回这个:

CarID | Make   | Model | CarID | Dist | U1 | U2 | U3 | U4 | U5 -- U22
23      Volvo    XC90    23      1170   68   93   24   23   19    93

到目前为止,我已经尝试过这样做,但它不起作用(我仍然从 dbo.CIS 表中获取两行)

SELECT
 c.CarID,
 c.Make,
 c.Model,
 cis.U1,
 cis.U2,
 cis.U3,
 cis.U4,
 cis.U5
 ....
 cis.U22
FROM
 dbo.Car c
LEFT JOIN
 (SELECT
  MAX(Dist) AS Dist,
  U1, 
  U2,
  U3, 
  U4, 
  U5
  ... 
  U22
  FROM dbo.CIS
  GROUP BY
  U1, 
  U2,
  U3, 
  U4, 
  U5
  ... 
  U22
  ) AS cis
ON
 cis.CarID = c.CarID
WHERE c.CarID = 23

我怎样才能只返回 1 行,其中包含来自 Car 表(顺便说一下没有重复)和 CIS 表中的信息,对于给定的@987654333,它可以有重复信息或根本没有信息@。我想从CIS 表中返回数据,当存在重复项时,Dist 列的值最高。

我应该补充一点,这只是一个涉及许多表和连接的更大 SQL 查询的 sn-p。因此,我需要任何解决方案来坐在同一个查询中,而不是单独创建临时表。

【问题讨论】:

  • 有人早些时候发布了一个使用 OUTER APPLY 的解决方案,这似乎对我有用。但他不知道为什么删除了那个答案。
  • 您最终使用了OUTER APPLY 解决方案吗?如果您这样做了,您可能希望将其添加为答案并接受它,如果它比下面肖恩的答案更好。

标签: sql sql-server sql-server-2012


【解决方案1】:

这是利用ROW_NUMBER 的一种方法。

with myCte as
(
    select ROW_NUMBER() over(partition by CarID order by Dist desc) as RowNum
        , CarID
        , Dist
        , U1
        , U2
    from CIS
)

select * --Your column names here
from Car c
join myCte cis on c.CarID = cis.CarID and cis.RowNum = 1

【讨论】:

    【解决方案2】:

    这使用公用表表达式来查找 dbo.CIS 表的匹配行条件。然后您可以使用carIDMax(dist) 轻松加入dbo.CIS

    WITH idMaxPair AS (
        SELECT carId, MAX(dist) as dist
        FROM dbo.CIS
        GROUP BY carId
    )
    SELECT
        c.CarID,
        c.Make,
        c.Model,
        cis.U1,
        cis.U2,
        cis.U3,
        cis.U4,
        cis.U5
        ....
        cis.U22
    FROM
        dbo.Car c
    LEFT JOIN idMaxPair
        ON idMaxPair.carId = c.CarID
    INNER JOIN dbo.CIS cis
        ON cis.carId = idMaxPair.carId
        AND cis.dist = idMaxPair.dist
    WHERE c.CarID = 23
    

    【讨论】:

    • 这比 Sean 的解决方案多使用了一个 JOIN。使用ROW_NUMBER,您可以在WITH 查询中直接从CIS 获取您需要的所有字段,并避免将该表加入两次。无论如何,顺便说一句,没有必要在IdMaxPair 上使用LEFT JOIN 而不是INNER,因为该表将包含所有CarIDs。
    • @Andrew 它确实使用了另一个连接。至于LEFT JOIN,我就这样离开了,因为原始查询对该表使用了左连接。感谢您的答案比较。
    【解决方案3】:

    既然您使用的是 SQL 2012,那么如何使用 FIRST_VALUE 窗口函数:

    SELECT DISTINCT
     c.CarID,
     c.Make,
     c.Model,
     FIRST_VALUE(cis.dist) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS dist,
     FIRST_VALUE(cis.U1) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS U1,
     FIRST_VALUE(cis.U2) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS U2,
     FIRST_VALUE(cis.U3) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS U3,
     FIRST_VALUE(cis.U4) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS U4,
     FIRST_VALUE(cis.U5) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS U5,
     FIRST_VALUE(cis.U22) OVER (PARTITION BY cis.CarId ORDER BY CIS.Dist DESC) AS U22
    FROM
     dbo.Car c
    LEFT JOIN dbo.CIS
        ON CIS.CarId = c.CarId
    WHERE c.CarID = 23 
    

    这会导致:

    CarID   Make    Model   dist    U1  U2  U3  U4  U5  U22
    23      Volvo   XC90    1170    68  93  24  23  19  93
    

    【讨论】:

    • 这里不需要子查询。您也可以将 FIRST_VALUE 添加到 cis.dist,然后在 Car 列上添加 group by。我很确定这最终会因额外的窗口功能而变慢。
    • @SeanLange 已更新,还没有机会进行任何性能测试
    猜你喜欢
    • 2013-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 2012-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-22
    相关资源
    最近更新 更多