0

I have this table and data

CREATE TABLE #transactions (
    [transactionId] [int] NOT NULL,
    [accountId] [int] NOT NULL,
    [dt] [datetime] NOT NULL,
    [balance] [smallmoney] NOT NULL,
 CONSTRAINT [PK_transactions_1] PRIMARY KEY CLUSTERED 
(   [transactionId] ASC)
) 

INSERT #transactions ([transactionId], [accountId], [dt], [balance]) VALUES 
(1, 1, CAST(0x0000A13900107AC0 AS DateTime), 123.0000),
(2, 1, CAST(0x0000A13900107AC0 AS DateTime), 192.0000),
(3, 1, CAST(0x0000A13A00107AC0 AS DateTime), 178.0000),
(4, 2, CAST(0x0000A13B00107AC0 AS DateTime), 78.0000),
(5, 2, CAST(0x0000A13D011D1860 AS DateTime), 99.0000),
(6, 2, CAST(0x0000A13F00000000 AS DateTime), 97.0000),
(7, 1, CAST(0x0000A13D0141E640 AS DateTime), 201.0000),
(8, 3, CAST(0x0000A1420094DD60 AS DateTime), 4000.0000),
(9, 3, CAST(0x0000A14300956A00 AS DateTime), 4100.0000),
(10, 3, CAST(0x0000A14700000000 AS DateTime), 4200.0000),
(11, 2, CAST(0x0000A14B00B84BB0 AS DateTime), 110.0000)

I need two queries.

  1. For each transaction, I want to return in a query the most recent balance for each account, and an extra column with a SUM of each account balance at that point in time.

  2. Same as 1 but grouped by date without the time portion. So the latest account balance at the end of each day (where there is a transaction in any account) for each account, but SUMed together as in 1.

Data above is sample data that I just made up, but my real table has hundreds of rows and ten accounts (which may increase soon). Each account has a unique accountId. Seems quite a tricky piece of SQL.

EXAMPLE

For 1. I need a result like this:

+---------------+-----------+-------------------------+---------+-------------+
| transactionId | accountId |           dt            | balance | sumBalances |
+---------------+-----------+-------------------------+---------+-------------+
|             1 |         1 | 2013-01-01 01:00:00.000 |     123 |         123 |
|             2 |         1 | 2013-01-01 01:00:00.000 |     192 |         192 |
|             3 |         1 | 2013-01-02 01:00:00.000 |     178 |         178 |
|             4 |         2 | 2013-01-03 01:00:00.000 |      78 |         256 |
|             5 |         2 | 2013-01-05 17:18:00.000 |      99 |         277 |
|             7 |         1 | 2013-01-05 19:32:00.000 |     201 |         300 |
|             6 |         2 | 2013-01-07 00:00:00.000 |      97 |         298 |
|             8 |         3 | 2013-01-10 09:02:00.000 |    4000 |        4298 |
|             9 |         3 | 2013-01-11 09:04:00.000 |    4100 |        4398 |
|            10 |         3 | 2013-01-15 00:00:00.000 |    4200 |        4498 |
|            11 |         2 | 2013-01-19 11:11:00.000 |     110 |        4511 |
+---------------+-----------+-------------------------+---------+-------------+

So, for transactionId 8, I take the latest balance for each account in turn and then sum them. AccountID 1: is 201, AccountId 2 is 97 and AccountId 3 is 4000. Therefore the result for transactionId 8 will be 201+97+4000 = 4298. When calculating the set must be ordered by dt

For 2. I need this

+------------+-------------+
|    date    | sumBalances |
+------------+-------------+
| 01/01/2013 |         192 |
| 02/01/2013 |         178 |
| 03/01/2013 |         256 |
| 05/01/2013 |         300 |
| 07/01/2013 |         298 |
| 10/01/2013 |        4298 |
| 11/01/2013 |        4398 |
| 15/01/2013 |        4498 |
| 19/01/2013 |        4511 |
+------------+-------------+

So on date 15/01/2013 the latest account balance for each account in turn (1,2,3) is 201,97,4200. So the result for that date would be 201+97+4200 = 4498

4

2 回答 2

3

这给出了您想要的第一个结果集(SQL Fiddle

WITH T
     AS (SELECT *,
                balance - 
                  isnull(lag(balance) OVER (PARTITION BY accountId 
                                             ORDER BY dt, transactionId), 0) AS B
         FROM   #transactions)
SELECT transactionId,
       accountId,
       dt,
       balance,
       SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM   T
ORDER  BY dt; 

它从以前的余额中减去帐户的当前余额以获得净差额,然后计算这些差额的总和。

这可以用作您的第二个结果的基础

WITH T1
 AS (SELECT *,
            balance - 
              isnull(lag(balance) OVER (PARTITION BY accountId 
                                         ORDER BY dt, transactionId), 0) AS B
     FROM   #transactions),
T2 AS (         
SELECT transactionId,
       accountId,
       dt,
       balance,
       ROW_NUMBER() OVER (PARTITION BY CAST(dt AS DATE) ORDER BY dt DESC, transactionId DESC) AS RN,
       SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM   T1)
SELECT CAST(dt AS DATE) AS [date], sumBalances
FROM T2
WHERE RN=1
ORDER  BY [date]; 
于 2013-05-11T20:35:17.617 回答
2

第1部分

; WITH a AS (
    SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
    FROM #transactions t
)
, b AS (
    SELECT t.*
    , transamount = t.balance - ISNULL(t0.balance,0)
    FROM a t
    LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY dt, transactionId)
FROM b
ORDER BY dt

第2部分

; WITH a AS (
    SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
    FROM #transactions t
)
, b AS (
    SELECT t.*
    , transamount = t.balance - ISNULL(t0.balance,0)
    FROM a t
    LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
, c AS (
    SELECT transactionId, accountId, dt, balance
    , sumBalance = SUM(transamount)OVER(ORDER BY CAST(dt AS DATE))
    , r1 = ROW_NUMBER()OVER(PARTITION BY accountId, CAST(dt AS DATE) ORDER BY dt DESC)
    FROM b
)
SELECT dt = CAST(dt AS DATE)
, sumBalance
FROM c
WHERE r1 = 1
ORDER BY CAST(dt AS DATE)
于 2013-05-11T20:50:34.757 回答