如何在不同条件下聚合两个不同列中的值?

Posted

技术标签:

【中文标题】如何在不同条件下聚合两个不同列中的值?【英文标题】:How to aggregate values in two different columns under different criteria? 【发布时间】:2021-07-05 18:47:34 【问题描述】:

这是我要处理的数据表:

CUST_REF ACC_NO JOINT_ACC PRODUCT NUM_OF_ACC CALC_FEE ACTUAL_FEE DIFF
100 ABC123 N ACCOUNT 4 140.68 104.14 36.54
100 ABC456 N STOCKS 4 41.72 30.24 11.48
100 XYZ123 N ISA 4 48.26 32.27 15.99
100 XYZ444 Y PENSION 4 3.15 0.00 3.15

我现在需要处理费用上限的影响,该上限因帐户是否为共同持有而异。 JOINT_ACC字段中的Y表示联名账户。

所有个人账户的费用上限为每位客户每月 166.67 (JOINT_ACC = 'N') - 即所有个人账户的总费用不能超过 166.67。但是,对于联名账户,上限适用于个人账户级别。换句话说,当 JOINT_ACC = 'Y' 时的 CALC_FEE 不能超过 166.67 - 在这种情况下,帐户的费用远低于 166.67,所以它保持原样(我不关心实际费用为 0,这是一个单独的问题伟大与美好)。

在此示例中,实际费用已经以某种方式考虑了此信息 - 如果您将 ACTUAL_FEE 列中的三个条目相加,则它们加起来为 166.67。

因此我想:

根据 JOINT_ACC 标志聚合 CALC_FEE 条目,并应用基于 CASE 的条件以达到费用上限 汇总个人账户和联名账户的 ACTUAL_FEE(我不想在此处应用费用上限,因为我的目标是将计算的费用与实际收取的费用进行比较)。

所以实际上我理想的输出表应该是这样的:

CUST_REF ACC_NO JOINT_ACC PRODUCT NUM_OF_ACC CALC_FEE ACTUAL_FEE DIFF
100 ABC123 N ACCOUNT 4 166.67 166.67 0.00
100 ABC456 N STOCKS 4 166.67 166.67 0.00
100 XYZ123 N ISA 4 166.67 166.67 0.00
100 XYZ444 Y PENSION 4 3.15 0.00 0.00

这是我迄今为止尝试过的:

    SELECT 
        A.CUST_REF,
        A.ACC_NO,
        A.JOINT_ACC,
        A.PRODUCT,
        A.NUM_OF_ACC,
        SUM(A.ACTUAL_FEE) OVER (PARTITION BY A.CUST_REF, A.ACC_NO, A.JOINT_ACC) AS FEES_CHARGED,
        
        CASE
           WHEN A.JOINT_ACC_IND = 'N'
             THEN
                (CASE
                    WHEN (SUM(B.CALC_FEE) OVER PARTITION BY (A.CUST_REF, A.ACC_NO)) > 166.67 THEN (166.67)
                    ELSE (SUM(B.CALC_FEE) OVER PARTITION BY (A.CUST_REF, A.ACC_NO))
                 END)
           WHEN A.JOINT_ACC_IND = 'Y'
             THEN
                (CASE
                    WHEN (C.CALC_FEE) > 166.67 THEN (166.67)
                    ELSE (C.CALC_FEE)
                 END)
           END
               AS ADJ_FEE_CALC,

         ((CASE
           WHEN A.JOINT_ACC_IND = 'N'
             THEN
                (CASE
                    WHEN (SUM(B.CALC_FEE) OVER PARTITION BY (A.CUST_REF, A.ACC_NO)) > 166.67 THEN (166.67)
                    ELSE (SUM(B.CALC_FEE) OVER PARTITION BY (A.CUST_REF, A.ACC_NO))
                 END)
           WHEN A.JOINT_ACC_IND = 'Y'
             THEN
                (CASE
                    WHEN (C.CALC_FEE) > 166.67 THEN (166.67)
                    ELSE (C.CALC_FEE)
                 END)
           END) - (SUM(A.ACTUAL_FEE) OVER (PARTITION BY A.CUST_REF, A.ACC_NO, A.JOINT_ACC))) AS DIFF

FROM V_FEES_TABLE A

     LEFT JOIN V_FEES_TABLE B ON A.CUST_REF = B.CUST_REF AND A.ACC_NO = B.ACC_NO AND B.JOINT_ACC = 'N'
     LEFT JOIN V_FEES_TABLE C ON A.CUST_REF = C.CUST_REF AND A.ACC_NO = C.ACC_NO AND C.JOINT_ACC = 'Y'

此查询需要很长时间才能运行(我在几分钟前检查时已超过一个小时)。显然,我在做一些根本错误/低效的事情。我不知道这是否有所不同,但V_FEES_TABLE 是一个构建在另一个视图之上的视图,该视图又引用了数据库中的核心表。

请帮忙!提前致谢。


编辑:

我有几种情况,下面建议的代码会引发误报:

CUST_REF ACC_NO JOINT_ACC PRODUCT NUM_OF_ACC CALC_FEE ACTUAL_FEE CUST_FEE_CALC ACTUAL_CUST_FEE_CHARGED DIFF
100 ABC123 N ACCOUNT 1 95.45 94.29 166.67 379.3 -212.63
100 ABC123 N ACCOUNT 1 95.45 95.36 166.67 379.3 -212.63

我用来重新运行该工作的代码是:

    SELECT 
            A.CUST_REF,
            A.ACC_NO,
            A.JOINT_ACC,
            A.PRODUCT,
            A.NUM_OF_ACC,
            A.CALC_FEE,
            A.ACTUAL_FEE,
    
    
    (CASE WHEN JOINT_ACC = 'Y' AND CALC_FEE < 166.67
                 THEN CALC_FEE
                 WHEN JOINT_ACC = 'Y'
                 THEN 166.67
                 WHEN SUM(CALC_FEE) OVER (PARTITION BY CUST_REF,    JOINT_ACC) < 166.67
                 THEN SUM(CALC_FEE) OVER (PARTITION BY CUST_REF, JOINT_ACC)            
                 ELSE 166.67
             END) as CUST_FEE_CALC,
    
            SUM(A.ACTUAL_FEE) OVER (PARTITION BY A.CUST_REF, A.JOINT_ACC) AS ACTUAL_CUST_FEE_CHARGED,

(CASE WHEN JOINT_ACC = 'Y' AND CALC_FEE < 166.67
                 THEN CALC_FEE
                 WHEN JOINT_ACC = 'Y'
                 THEN 166.67
                 WHEN SUM(CALC_FEE) OVER (PARTITION BY CUST_REF,    JOINT_ACC) < 166.67
                 THEN SUM(CALC_FEE) OVER (PARTITION BY CUST_REF, JOINT_ACC)            
                 ELSE 166.67
             END) - SUM(A.ACTUAL_FEE) OVER (PARTITION BY A.CUST_REF, A.JOINT_ACC) 
              as DIFF

FROM FEES_TABLE A

在某些情况下,同一帐户在同一时期内被计费两次,金额不同 - 实际上,SQL 将其视为两个单独的帐户以进行聚合。这在不经意间扭曲了我自己的计算,因为它加起来是 95.45 的两倍,客户级别的费用为 166.67,考虑到上面基于 Gordon 的解决方案的上限。

我希望 SQL 将计算的客户费用保留在帐户级别汇总,但将实际收取的费用加起来,因为我不确定为什么我在这里看到 94.29 和 95.36 的不同数字。因此,我想看看:

CUST_REF ACC_NO JOINT_ACC PRODUCT NUM_OF_ACC CALC_FEE ACTUAL_FEE CUST_FEE_CALC ACTUAL_CUST_FEE_CHARGED DIFF
100 ABC123 N ACCOUNT 1 95.45 94.29 95.45 189.65 -94.20
100 ABC123 N ACCOUNT 1 95.45 95.36 95.45 189.65 -94.20

我尝试修改PARTITION BY 条件以也包括ACC_NO 列,但没有成功。有什么想法吗?

【问题讨论】:

如果您想让帮助变得真正容易,请将您的示例数据提供为 DDL+DML(或小提琴)。 您好...不幸的是,由于设备不同,我只能在上面的问题中提供我的示例数据 - 如果我可以进一步澄清我的查询,请告诉我.. 我找到了根本原因;我的分区能够与您的代码一起修复的源数据存在问题!非常感谢。 【参考方案1】:

我不明白为什么需要任何连接,只是窗口函数。要获得计算的费用:

SELECT FT.*,
       (CASE WHEN JOINT_ACC = 'Y' AND CALC_FEE < 166.67
             THEN CALC_FEE
             WHEN JOINT_ACC = 'Y'
             THEN 166.67
             WHEN SUM(CALC_FEE) OVER (PARTITION BY CUST_REF, JOINT_ACC) < 166.67
             THEN SUM(CALC_FEE) OVER (PARTITION BY CUST_REF, JOINT_ACC)            
             ELSE 166.67
         END) as IMPUTED_CALC_FEE
FROM V_FEES_TABLE FT

【讨论】:

确实非常感谢 - 这肯定有助于我进行聚合 - 尽管我注意到了一个我以前从未见过的极端情况,这导致了一些误报。请您看看我上面的编辑,如果您能就如何调整我的聚合标准提供建议,请告诉我?

以上是关于如何在不同条件下聚合两个不同列中的值?的主要内容,如果未能解决你的问题,请参考以下文章

聚合后如何有条件地对来自不同列的值求和?

如何突出显示数据框的两个不同列中的唯一数据值?

TSQL - 如何在插入之前根据不同列中的值检查值?

如何在 BigQuery 的标准 SQL 中解析具有不同日期字符串的列中的值

如何在具有相同 ID 的列中选择不同的值然后删除它们 PHP SQL Server

如何创建临时表或仅从循环中的列中选择不同的值