0

I'm a bit stumped about how to solve this particular piece of a problem I'm working on. I started with a much bigger problem, but I managed to simplify it into this while keeping good performance intact.

Say I have the following result set. AggregateMe is something I'm deriving from SQL conditionals.

MinutesElapsed   AggregateMe    ID    Type   RowNumber
1480             1              1     A      1
1200             0              1     A      2
1300             0              1     B      3
1550             0              1     C      4
725              1              1     A      5
700              0              1     A      6
1900             1              2     A      7
3300             1              2     A      8
4900             0              2     A      9

If AggregateMe is 1 (true) or, if you prefer, if is true, I want the counts to be aggregated into the next row where AggregateMe (or conditions) do not evaluate to true. Aggregate functions or Subqueries are fair game as is PARTITION BY.

For example, the above result set would become:

MinutesElapsed     ID   Type
2680               1    A       
1300               1    B       
1550               1    C       
1425               1    A       
10100              2    A       

Is there a clean way to do this? If you want, I can share more about the original problem, but it is a bit more complicated.

Edited to add: SUM and GROUP BY alone won't work, because some sums would be rolled into the wrong row. My sample data did not reflect this case, so I added rows where this case can occur. In the updated sample data, using an aggregate function in the simplest way would cause the 2680 count and the 1425 count to be rolled together, which I do not want.

EDIT: And if you're wondering how I got here in the first place, here you go. I'm going to aggregate statistics about how long our program left something in a certain ActionType, and my first step was by creating this subquery. Please feel free to criticize:

select 
    ROW_NUMBER() over(order by claimid, insertdate asc) as RowNbr,
    DateDiff(mi, ahCurrent.InsertDate, CASE WHEN ahNext.NextInsertDate is null THEN GetDate() ELSE ahNext.NextInsertDate END) as MinutesInActionType,
    ahCurrent.InsertDate, ahNext.NextInsertDate,
    ahCurrent.ClaimID, ahCurrent.ActionTypeID,
    case when ahCurrent.ActionTypeID = ahNext.NextActionTypeID and ahCurrent.ClaimID = ahNext.NextClaimID then 1 else 0 end as aggregateme
    FROM 
    (
        select ROW_NUMBER () over(order by claimid, insertdate asc) as RowNum, ClaimID, InsertDate, ActionTypeID
        From autostatushistory
        --Where AHCurrent is not AHPast
    ) ahCurrent
    LEFT JOIN
    (
        select ROW_NUMBER() over(order by claimid, insertdate asc) as RowNum, ClaimID as NextClaimID, InsertDate as NextInsertDate, ActionTypeID as NextActionTypeID
        FROM autostatushistory
    ) ahNext 
    ON (ahCurrent.ClaimID = ahNext.NextClaimID AND ahCurrent.RowNum = ahNext.RowNum - 1 and ahCurrent.ActionTypeID = ahNext.NextActionTypeID)
4

2 回答 2

1

这里是你需要执行的查询,它不干净,也许你会优化它:

WITH cte AS( /* Create a table containing row number */
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ROW,
       MinutesElapsed,
       AggregateMe,
       ID,
       TYPE
    FROM rolling
)
SELECT MinutesElapsed + (CASE          /* adding minutes from next valid records*/
                         WHEN  cte.AggregateMe <> 1   /*if current record is 0 then */
                         THEN 0                        /*skip it*/
                         ELSE  
                             (SELECT SUM(MinutesElapsed) /* calculating sum of all -> */
                             FROM cte localTbl
                             WHERE
                                  cte.ROW < localTbl.ROW /* next records -> */
                                AND
                                  localTbl.ROW <= (      /* until we find aggregate = 0 */
                                                   SELECT MIN(ROW) 
                                                   FROM cte sTbl
                                                   WHERE sTbl.AggregateMe = 0
                                                         AND
                                                         sTbl.ROW > cte.ROW
                                                   )
                                AND 
                                  (localTbl.AggregateMe = 0 OR /* just to be sure :) */
                                    localTbl.AggregateMe = 1))
                          END) as MinutesElapsed,
       AggregateMe,
       ID,
       TYPE
FROM cte
WHERE cte.ROW = 1 OR NOT(       /* not showing records used that are used in sum, skipping 1 record*/           
    (                          /* records with agregate 0 after record with aggregate 1 */
      cte.AggregateMe = 0 
      AND
      (
        SELECT AggregateMe
        FROM cte tblLocal
        WHERE cte.ROW = (tblLocal.ROW + 1)
      )>0
    ) 
  OR
     (   /* record with aggregate 1 after record with aggregate 1 */
      cte.AggregateMe = 1 
      AND
      (
        SELECT AggregateMe
        FROM cte tblLocal
        WHERE cte.ROW = (tblLocal.ROW + 1)
      )= 1
    )
);

在这里测试

希望对您的问题有所帮助。随意问的问题。

于 2013-09-25T17:06:02.297 回答
0

通过查看您的结果集,以下内容似乎可行,

SELECT ID,Type,SUM(MinutesElapsed)
FROM mytable
GROUP BY ID,Type

但是如果不查看原始数据集就无法确定。

于 2013-09-25T15:13:53.370 回答