[英]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;
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.