【问题标题】:How to self join/group by to get sum?如何自行加入/分组以获取总和?
【发布时间】:2021-06-04 18:28:09
【问题描述】:

我有一张桌子:

ID      GroupID  Contact  Subject  Score
10      32       8017      5        77
11      15       5019      1        80
12      32       8018      3        62
13      17       8870      9        63
14      49       8018      11       72
15      19       8305      7        93
16      22       8029      11       88

只要GroupIDContactSubject 三个字段中的每一个之间有共同的值,我希望得到每个IDScore 的总和,以及共同的ID .

例如,

  • ID 10 与 ID 12 相关联,因为它们具有相同的 GroupID
  • ID 12 随后与 ID 14 相关联,因为它们具有相同的 Contact
  • ID 14 随后与 ID 16 相关联,因为它们具有相同的 Subject
  • 因此,Sum_Score 的 ID 10121416 = 77 + 62 + 72 + 88 = 299

输出:

ID      GroupID  Contact  Subject  Score  Sum_Score   Common_IDs
10      32       8017      5        77      299       (10, 12, 14, 16)
11      15       5019      1        80      80        (11)
12      32       8018      3        62      299       (10, 12, 14, 16)
13      17       8870      9        63      63        (13)
14      49       8018      11       72      299       (10, 12, 14, 16)
15      19       8305      7        93      93        (15)
16      22       8029      11       88      299       (10, 12, 14, 16)

【问题讨论】:

    标签: sql pandas recursion join aggregate


    【解决方案1】:

    这可能可以进一步优化,但效果很好,与 pandas 和 networkx;

    columns = ['GroupID','Contact','Subject']
    G = nx.empty_graph(0, nx.MultiGraph())
    for i in range(len(columns)-1):
        G.add_edges_from(zip(df[columns[i]], df[columns[i+1]]))
        
    s = pd.Series(nx.connected_components(G)).map(list).explode()
    u = df.assign(k=df[columns].stack().map({v:k for k,v in s.items()}).max(level=0))
    out = (u.merge(u.groupby('k').agg(Common_IDs=('ID',tuple),Sum_Score=('Score','sum'))
           ,left_on='k',right_index=True,how='left')).drop('k',1)
    

    print(out)
    
       ID  GroupID  Contact  Subject  Score        Common_IDs  Sum_Score
    0  10       32     8017        5     77  (10, 12, 14, 16)        299
    1  11       15     5019        1     80             (11,)         80
    2  12       32     8018        3     62  (10, 12, 14, 16)        299
    3  13       17     8870        9     63             (13,)         63
    4  14       49     8018       11     72  (10, 12, 14, 16)        299
    5  15       19     8305        7     93             (15,)         93
    6  16       22     8029       11     88  (10, 12, 14, 16)        299
    

    【讨论】:

    • 急切地等待 python 解决方案:D 这看起来不错。
    • @Pygirl 很久没有使用 networkx 了,好像我忘记了功能。不确定我们是否可以更好地构图,但会在晚上晚些时候检查。谢谢你:)
    【解决方案2】:

    架构

    create table tablename(ID int,      GroupID int,  Contact int,  Subject int,  Score int);
    insert into tablename values(10,     32,      8017,     5,       77);
    insert into tablename values(11,     15,      5019,     1,       80);
    insert into tablename values(12,     32,      8018,     3,       62);
    insert into tablename values(13,     17,      8870,     9,       63);
    insert into tablename values(14,     49,      8018,     11,      72);
    insert into tablename values(15,     19,      8305,     7 ,      93);
    insert into tablename values(16,     22,      8029,     11,      88);
    

    查询 #1 (SQL Server)

    with cte as (
    select id rid,* from tablename 
    union all
    select cte.rid rid,t.* from tablename t inner join cte on (t.groupid=cte.groupid or t.contact=cte.contact or t.subject=cte.subject) and t.id>cte.id
    ),
    finalcte as(
    select  id,groupid,contact,subject,sum(score)over(partition by rid) TotalScore from cte)
    select t.id,t.groupid,t.contact,t.subject,SumScore.TotalScore from tablename t 
    outer apply (select max(TotalScore)TotalScore from finalcte c where t.id=c.id) SumScore
    

    查询 #2(MySQL v8.0 和 PostgreSQL)

      WITH RECURSIVE cte as (
    select id rid,id,groupid,contact,subject,score from tablename 
    union all
    select cte.rid rid,t.id,t.groupid,t.contact,t.subject,t.score from tablename t inner join cte on (t.groupid=cte.groupid or t.contact=cte.contact or t.subject=cte.subject) and t.id>cte.id
    ),
    finalcte as(
    select  id,groupid,contact,subject,sum(score)over(partition by rid) TotalScore from cte)
    
    select t.id,t.groupid,t.contact,t.subject,max(TotalScore) from tablename t inner join finalcte c on t.id=c.id
    group by t.id,t.groupid,t.contact,t.subject
    order by t.id;
    

    查询 #3 Oracle

    with cte (rid,id,groupid,contact,subject,score) as (
    select id rid,id,groupid,contact,subject,score from tablename 
    union all
    select cte.rid rid,t.id,t.groupid,t.contact,t.subject,t.score from tablename t inner join cte on (t.groupid=cte.groupid or t.contact=cte.contact or t.subject=cte.subject) and t.id>cte.id
    ),
    finalcte as(
    select  id,groupid,contact,subject,sum(score)over(partition by rid) TotalScore from cte)
    
    select t.id,t.groupid,t.contact,t.subject,SumScore.TotalScore from tablename t 
    outer apply (select max(TotalScore)TotalScore from finalcte c where t.id=c.id) SumScore
    order by t.id
    

    输出:

    id groupid contact subject max(TotalScore)
    10 32 8017 5 299
    11 15 5019 1 80
    12 32 8018 3 299
    13 17 8870 9 63
    14 49 8018 11 299
    15 19 8305 7 93
    16 22 8029 11 299

    View on DB Fiddle

    【讨论】:

      猜你喜欢
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-15
      相关资源
      最近更新 更多