繁体   English   中英

从SQL获取单个MAX值+另一个列值的最佳方法

[英]Best way to get single MAX value + another column value from SQL

我定义了以下三个表:

table 'A':
-------------------
majorID    | bigint (primary key)
-------------------

table 'B':
-------------------
majorID    | bigint (foreign key to table 'A's majorID)
minorID    | bigint (primary key)
totalSize  | bigint
-------------------

table 'C':
-------------------
objectID   | bigint (primary key)
majorID    | bigint (foreign key to table 'A's majorID)
minorID    | bigint (foreign key to table 'B's minorID)
startPoint | bigint
length     | bigint
-------------------

我想要做的是获取表'B'中所有行的列表,但要显示每行还有多少空间。

可以通过找到最高的“ startPoint”,为包含最高“ startPoint”的行的“ length”列添加值,然后从表“ B”的“ totalSize”列中减去该组合值来找到剩余空间。

我目前可以使用以下代码实现此目的:

create table #results (MinorID bigint, MajorID bigint, RemainingSpace bigint)

DECLARE @MinorID bigint
DECLARE @TotalSpace bigint
DECLARE @MajorID bigint
DECLARE cur CURSOR FOR
SELECT MinorID, MajorID, TotalSize FROM B
OPEN cur


FETCH NEXT FROM cur INTO @MinorID,@MajorID, @TotalSpace

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @UsedSize bigint

    SELECT TOP 1 @UsedSize = StartPoint + [length] FROM C
    WHERE MinorID = @MinorID AND MajorID = @MajorID
    ORDER BY StartPoint DESC

    INSERT INTO #results VALUES (@MinorID,@MajorID,@TotalSpace - @UsedSize)

    FETCH NEXT FROM cur INTO @MinorID,@MajorID, @TotalSpace
END

CLOSE cur
DEALLOCATE cur

SELECT * FROM #results
drop table #results       

问题是我希望这些表会变得很大,而且我意识到在表上运行游标可能不是实现我想要的最快方法。

但是,我一直在努力寻找更好的解决方案(星期一早晨忧郁),并希望有人比我更清醒/更好地提出SQL解决方案!

注意:表的设计不是“一成不变的”,因此,如果唯一的解决方案是对数据进行非规范化,以便表“ B”保留其“已占用空间”的记录,那么我很乐意...

编辑:

我对接受的答案进行了修改,如下所示:

SELECT B.*, coalesce(C.StartPoint + C.Length,0) AS UsedSize
    FROM TableB B
    LEFT JOIN 
    (
      SELECT *, DENSE_RANK() OVER(PARTITION BY C.MajorID, C.MinorID ORDER BY C.StartPoint DESC) AS Rank
      FROM TableC C
    ) C
    ON C.MajorID = B.MajorID
    AND C.MinorID = B.MinorID
    AND C.Rank = 1

也许您可以使用DENSE_RANK

在此查询中,我将表C与额外的列Rank结合在一起。 如果此列的最高起点为1,则其值为1。 在(AND C.Rank = 1)中,我们仅提取该行。

SELECT B.*, (C.StartPoint + C.Length) AS UsedSize
FROM TableB B
INNER JOIN 
(
  SELECT *, DENSE_RANK() OVER(PARTITION BY C.MajorID, C.MinorID ORDER BY C.StartPoint DESC) AS Rank
  FROM TableC C
) C
ON C.MajorID = B.MajorID
AND C.MinorID = B.MinorID
AND C.Rank = 1
WITH UsedSpace AS
(
SELECT minorID, MAX(startPoint + length) AS used
FROM C
GROUP BY minorID
)
SELECT B.minorID, totalSize - COALESCE(UsedSpace.used, 0)
FROM B LEFT JOIN UsedSpace ON B.minorID = UsedSpace.minorID

也许您使事情变得比他们复杂。 您正在寻找每个minorID的最大startPoint,以增加长度并因此获得使用的大小。 但是,如果起点的长度如此之大,以至于两者相加会超过最大起点和长度之和,那么根本不可能有一个较小的起点吗?

这是否可能(max(startPoint)低于其他一些startPoint + length):

minorID startPoint length
1       1          10
1       9          3         

如果没有,我假设,您可以简单地减去max(startPoint + length):

select 
  minorID, 
  totalSize, 
  totalSize - (select max(startPoint + length) from C where C.minorID = B.minorID) as space_left
from B;

编辑:我只是读了你的评论有时B的C不存在。 为了解决这个问题,您将必须使用ISNULL或COALESCE:

select 
  minorID, 
  totalSize, 
  totalSize - coalesce((select max(startPoint + length) from C where C.minorID = B.minorID), 0) as space_left
from B;

您可以使用OUTER APPLY来获取StartPoint排序的C中的最高记录

SELECT  B.MajorID,
        B.MinorID,
        B.TotalSize,
        C.StartPoint,
        C.Length,
        SpaceRemaining = B.TotalSize - ISNULL(C.StartPoint + C.Length, 0)
FROM    B
        OUTER APPLY
        (   SELECT  TOP 1 C.StartPoint, C.Length
            FROM    C
            WHERE   B.MinorID = c.MinorID
            ORDER BY C.StartPoint DESC
        ) C;

或者,您可以使用ROW_NUMBER来获得相同的结果,具体取决于索引等,其中一个的性能可能会优于另一个:

SELECT  B.MajorID,
        B.MinorID,
        B.TotalSize,
        C.StartPoint,
        C.Length,
        SpaceRemaining = B.TotalSize - ISNULL(C.StartPoint + C.Length, 0)
FROM    B
        LEFT JOIN 
        (   SELECT  C.MinorID,
                    C.StartPoint, 
                    C.Length,
                    RowNumber = ROW_NUMBER() OVER(PARTITION BY C.MinorID ORDER BY C.StartPoint DESC, Length DESC)
            FROM    C
        ) C
            ON B.MinorID = c.MinorID
            AND C.Rownumber = 1;

SQL小提琴上的示例

SELECT B.MajorId, B.MinorId, B.totalSize-(C.length+C.startPoint) as Space 
from TABLEB B 
LEFT JOIN (SELECT MAX(startPoint) maxSP,majorid, minorid FROM TABLEC GROUP BY MajorId, MinorId) 
mxT ON B.majorID  = mxT.majorID AND B.minorId=mxt.minorId 
LEFT JOIN TABLEC C  on C.majorid=mxt.MajorId AND C.minorId=mxt>MinorId AND C.startPoint=mxT.maxSP

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM