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

时间:2014-01-27 11:01:03

标签: sql sql-server database

我已经定义了以下三个表:

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

5 个答案:

答案 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 APPLYStartPoint

订购的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;

<强> 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