我正在尝试对发生在借方之前的所有贷方进行总计,然后对4天之内贷方之后的所有借方进行总计。
表格
ACCT |Date | Amount | Credit or debit
-----+----------+---------+----------------
152 |8/14/2017 | 48 | C
152 |8/12/2017 | 22.5 | D
152 |8/12/2017 | 40 | D
152 |8/11/2017 | 226.03 | C
152 |8/10/2017 | 143 | D
152 |8/10/2017 | 107.23 | C
152 |8/10/2017 | 20 | D
152 |8/10/2017 | 49.41 | C
我的查询仅应在借方之前有贷方的情况下求和。结果将包含上面数据的3行。
所需的输出:
acct DateRange credit_amount debit_amount
--------------------------------------------------------------------------
152 2017-10-14 to 2017-10-18 49.41 20
152 2017-10-14 to 2017-10-18 107.23 143
152 2017-10-14 to 2017-10-18 226.03 62.5
最后一个是对两个借方求和,直到有贷方为止。
我只需要信贷日期在借方日期之前的情况。 8/14上的48被忽略,因为其后没有借方。
逻辑是查看帐户是否已贷记,然后再扣除。
我的尝试
DECLARE @StartDate DATE
DECLARE @EndDate DATE
DECLARE @OverallEndDate DATE
SET @OverallEndDate = '2017-08-14'
SET @StartDate = '2017-08-10'
SET @EndDate = dateadd(dd, 4, @startDate);
WITH Dates
AS (
SELECT @StartDate AS sd, @EndDate AS ed, @OverallEndDate AS od
UNION ALL
SELECT dateadd(dd, 1, sd), DATEADD(dd, 1, ed), od
FROM Dates
WHERE od > sd
), credits
AS (
SELECT DISTINCT A.Acct, LEFT(CONVERT(VARCHAR, @StartDate, 120), 10) + 'to' + LEFT(CONVERT(VARCHAR, @EndDate, 120), 10) AS DateRange, credit_amount, debit_amount
FROM (
SELECT t1.acct, sum(amount) AS credit_amount, MAX(t1.datestart) AS c_datestart
FROM [Transactions] T1
WHERE Credit_or_debit = 'C' AND T1.Datestart BETWEEN @StartDate AND @EndDate AND T1.[acct] = '152' AND T1.Datestart <= (
SELECT MIN(D1.Datestart)
FROM [Transactions] D1
WHERE T1.acct = D1.acct AND D1.Credit_or_debit = 'D' AND D1.Datestart BETWEEN @StartDate AND @EndDate
)
GROUP BY T1.acct
) AS A
CROSS JOIN (
SELECT t2.acct, sum(amount) AS debit_amount, MAX(t2.datestart) AS c_datestart
FROM [Transactions] T2 AND T2.DBCR = 'D' AND T2.Datestart BETWEEN @StartDate AND @EndDate AND T2.[acct] = '152' AND T2.Datestart <= (
SELECT MAX(D1.Datestart)
FROM [Transactions] D1
WHERE T2.acct = D1.acct AND D1.Credit_or_debit = 'D' AND D1.Datestart BETWEEN @StartDate AND @EndDate
)
GROUP BY T2.acct
) AS B
WHERE A.acct = B.acct AND A.c_datestart <= B.d_datestart
)
SELECT *
FROM credits
OPTION (MAXRECURSION 0)
更新:
存储的日期实际上是带有时间戳的日期。这就是我验证借方是否>贷方的方式。
答案 0 :(得分:-1)
现在应该清楚地知道,您肯定需要指定一列交易顺序的列,因为否则,当它们都具有相同的datestart
时,您将无法决定是在借方之前还是之后记入借方。假设您有这样一列(在我的查询中,我将其命名为ID
),则可以采用以下解决方案,而无需递归并且也不需要自连接。可以使用SQL Server 2008以来提供的一些 window函数来解决该问题。
我的解决方案按照几个步骤来处理数据,这些步骤由2个CTE和最终的PIVOT
查询序列实现:
DECLARE @StartDate DATE = '20170810';
DECLARE @EndDate DATE = dateadd(dd, 4, @StartDate);
DECLARE @DateRange nvarchar(24);
SET @DateRange =
CONVERT(nvarchar(10), @StartDate, 120) + ' to '
+ CONVERT(nvarchar(10), @EndDate, 120);
WITH
blocks (acct, CD, amount, blockno, r_blockno) AS (
SELECT acct, Credit_or_debit, amount
, ROW_NUMBER() OVER (PARTITION BY acct ORDER BY ID ASC)
- ROW_NUMBER() OVER (PARTITION BY acct, Credit_or_debit ORDER BY ID ASC)
, ROW_NUMBER() OVER (PARTITION BY acct ORDER BY ID DESC)
- ROW_NUMBER() OVER (PARTITION BY acct, Credit_or_debit ORDER BY ID DESC)
FROM Transactions
WHERE datestart BETWEEN @StartDate AND @EndDate
AND Credit_or_debit IN ('C','D') -- not needed, if always true
),
blockpairs (acct, CD, amount, pairno) AS (
SELECT acct, CD, amount
, DENSE_RANK() OVER (PARTITION BY acct, CD ORDER BY blockno)
FROM blocks
WHERE (blockno > 0 OR CD = 'C') -- remove leading debits
AND (r_blockno > 0 OR CD = 'D') -- remove trailing credits
)
SELECT acct, @DateRange AS DateRange
, amt.C AS credit_amount, amt.D AS debit_amount
FROM blockpairs PIVOT (SUM(amount) FOR CD IN (C, D)) amt
ORDER BY acct, pairno;
这是它的工作方式:
阻止
此处,从表中检索了相关数据,这意味着应用了日期范围过滤器,并且Credit_or_debit
列上的另一个过滤器确保结果中仅包含值C和D(如果表格中的情况就是这样,那么WHERE
子句的那一部分可以省略)。此CTE的必要部分是两个行号(blockno
)之差。贷方和借方分别编号,并从总行号中减去它们各自的行号。在连续的借方或贷方块中,每个记录的这些数字将相同,而在以后的同一类型的块中,这些数字将不同(较高)。此编号的主要用途是识别第一个块(编号0
),以便能够将其排除在外
如果是借方冻结,则在下一步中进行进一步处理。为了能够识别最后一块(如果是贷方块,则将其过滤掉),以相反的顺序进行编号(r_blockno
)。结果(我订购的只是为了使用您的示例数据进行可视化)将如下所示:
块对
如前所述,在此CTE中,如果第一个块是借方块,则将其过滤掉,如果是贷方块,则将最后一个块滤除。这样做,剩余的块数必须是偶数,并且块的逻辑顺序必须是一系列贷方和借方块,每对从贷方块开始,然后是其相关的借方块。每对贷方/借方块最后都会导致一行。为了在查询中正确关联贷方和借方冻结,我通过使用每种类型的单独编号(第 n 个贷方冻结和 n -th借记卡块的编号相同( n )来关联它们。对于此编号,我使用DENSE_RANK
函数,使块中的所有记录都获得相同的编号(pairno
),并使编号无间隙。为了对相同类型的块进行编号,我将上述的blockno
字段重新用于排序。示例中的结果(再次进行可视化排序):
最终的PIVOT查询
最后,将credit_amount
和debit_amount
汇总到按acct
和pairno
分组的各个块上,然后使用{{1}并排显示}查询。
尽管PIVOT
列不可见,但它用于对结果记录进行排序。