我已经定义了以下三个表:
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”列的值,然后从表中的“totalSize”列中减去该组合值B'
我目前能够使用以下代码实现此目的:
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
答案 0 :(得分:4)
也许您可以使用DENSE_RANK。
在此查询中,我将使用额外列Rank加入表C.如果该列的最高起点,则该列的值为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
答案 1 :(得分:2)
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
答案 2 :(得分:2)
也许你让事情变得更复杂。您正在寻找每个minorID的最大startPoint,以便增加长度并获得使用的大小。但是,它的长度是如此之大以至于加上两者都会超过最大startPoint加上它的长度,它是否有可能有一个较小的startPoint?
这是否可能(max(startPoint)低于其他某个startPoint +长度):
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;
答案 3 :(得分:1)
您可以使用OUTER APPLY从StartPoint
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;
<强> Examples on SQL Fiddle 强>
答案 4 :(得分:0)
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