找到满足两个不同聚合标准的记录的更好方法?

Better way to find records meeting two different aggregate criteria?

我需要找到贷方交易总和大于或等于阈值金额并且借方交易总和小于或等于该阈值的倒数的客户相同的门槛金额。我还想 return 总交易金额满足信用和借记阈值的交易。我想出了两个解决方案,但我认为我可能会使事情变得比必要的更复杂。

这是一些简化的示例数据...

TransactionID  CustomerID  TransactionDate  TransactionType  TransactionAmount
-------------  ----------  ---------------  ---------------  -----------------
1              1           2020-10-01       Credit           10.25
2              1           2020-10-02       Credit           11.50
3              1           2020-10-02       Debit            -13.25
4              2           2020-10-01       Credit           14.22
5              2           2020-10-02       Debit            -50.75
6              2           2020-10-04       Credit           12.85
7              2           2020-10-07       Debit            -4.53
8              3           2020-10-02       Credit           12.85

门槛金额为 20 美元 CustomerID 2 是唯一同时满足贷方和借方门槛的客户。所以这是我的预期结果...

TransactionID  CustomerID  TransactionDate  TransactionType  TransactionAmount
-------------  ----------  ---------------  ---------------  -----------------
4              2           2020-10-01       Credit           14.22
6              2           2020-10-04       Credit           12.85
5              2           2020-10-02       Debit            -50.75
7              2           2020-10-07       Debit            -4.53

我想出了两个解决方案。我应该提到,由于我最终需要在其中嵌入此查询的软件,我无法使用临时 tables 或 table 变量。

解决方案#1

SELECT
         txn.TransactionID
       , txn.CustomerID
       , txn.TransactionDate
       , txn.TransactionType
       , txn.TransactionAmount
FROM     Transactions txn
WHERE    EXISTS (
                    SELECT
                             txn1.CustomerID
                           , SUM(txn1.TransactionAmount) AS [TransactionAmoutTotal]
                    FROM     Transactions txn1
                    WHERE    txn1.TransactionType = 'Credit'
                         AND txn1.CustomerID      = txn.CustomerID
                    GROUP BY txn1.CustomerID
                    HAVING   SUM(txn1.TransactionAmount) >= @ThresholdAmount
                )
     AND EXISTS (
                    SELECT
                             txn2.CustomerID
                           , SUM(txn2.TransactionAmount) AS [TransactionAmoutTotal]
                    FROM     Transactions txn2
                    WHERE    txn2.TransactionType = 'Debit'
                         AND txn2.CustomerID      = txn.CustomerID
                    GROUP BY txn2.CustomerID
                    HAVING   SUM(txn2.TransactionAmount) <= (@ThresholdAmount * -1)
                )
ORDER BY txn.CustomerID
       , txn.TransactionType
       , txn.TransactionDate;

解决方案 #2

WITH txn_cte (TransactionID, CustomerID, TransactionDate, TransactionType, TransactionAmount, TransactionAmountTotal) AS
(
    SELECT
          txn.TransactionID
        , txn.CustomerID
        , txn.TransactionDate
        , txn.TransactionType
        , txn.TransactionAmount
        , txn.TransactionAmountTotal
    FROM  (
              SELECT
                   TransactionID
                 , CustomerID
                 , TransactionDate
                 , TransactionType
                 , TransactionAmount
                 , SUM(TransactionAmount) OVER (PARTITION BY CustomerID, TransactionType) AS [TransactionAmountTotal]
              FROM Transactions
          ) txn
    WHERE ABS(txn.TransactionAmountTotal) >= @ThresholdAmount
)
SELECT
         txn_cte.TransactionID
       , txn_cte.CustomerID
       , txn_cte.TransactionDate
       , txn_cte.TransactionType
       , txn_cte.TransactionAmount
FROM     txn_cte
WHERE    EXISTS (
                    SELECT txn_cte1.TransactionID
                    FROM   txn_cte txn_cte1
                    WHERE  txn_cte1.TransactionType        = 'Credit'
                       AND txn_cte1.CustomerID             = txn_cte.CustomerID
                )
     AND EXISTS (
                    SELECT txn_cte2.TransactionID
                    FROM   txn_cte txn_cte2
                    WHERE  txn_cte2.TransactionType        = 'Debit'
                       AND txn_cte2.CustomerID             = txn_cte.CustomerID
                )
ORDER BY txn_cte.CustomerID
       , txn_cte.TransactionType
       , txn_cte.TransactionDate;

这是我的示例数据插入的 dbfiddle 和两个解决方案。鉴于我不能使用 temp tables 或 table 变量,有没有更好的方法来做到这一点?

您可以使用 window 函数:

select transactionid, customerid, transactiondate, transactiontype, transactionamount
from (
    select t.*,
        sum(case when transactiontype = 'Credit' then transactionamount else 0 end) 
            over(partition by customerid) sum_credit,
        sum(case when transactiontype = 'Debit' then transactionamount else 0 end) 
            over(partition by customerid) sum_debit
    from transactions t
) t
where sum_credit > @ThresholdAmount and sum_debit < - @ThresholdAmount
order by customerid, transactiontype, transactiondate

在子查询中,条件总和计算每个客户的总借方和贷方。然后您可以使用此信息在外部查询中进行过滤。

也许 HAVING 中的某些条件聚合是您所追求的?虽然这 returns 没有行,因为没有客户有 “大于阈值金额的贷方交易总和以及大于同一阈值金额的借方交易总和”。那是因为所有借记交易都有负值,负值的 SUM 永远不会是正数(因此永远不会大于示例中的 20 的值):

DECLARE @ThresholdAmount NUMERIC(10, 2);
SET @ThresholdAmount = 20.00;

SELECT CustomerID
FROM dbo.Transactions
GROUP BY CustomerID
HAVING SUM(CASE TransactionType WHEN 'Credit' THEN TransactionAmount END) > @ThresholdAmount
   AND SUM(CASE TransactionType WHEN 'Debit' THEN TransactionAmount END) > @ThresholdAmount;