【问题标题】:SQL like: How to calculate intersection and union of <item,user> dataSQL like:如何计算 <item,user> 数据的交集和并集
【发布时间】:2018-05-18 09:10:14
【问题描述】:

在 SQL 方面需要帮助:

我有一个包含以下列的数据:

  • 物品编号
  • 用户ID

每一行表示某个用户购买了某些商品。 示例:

ItemId UserId

   200    user1

   200    user3

   200    user4

   300    user5

   300    user3

对于每一个我想计算如下输出表:

  • users(i) : 购买 i 的用户数
  • users(j) : 购买 j 的用户数
  • users(i, j) : 同时购买 i 和 j 的用户数
  • users(i, ~j) : 购买 i 但未购买 j 的用户数
  • users(~i, j) : 购买 j 但未购买 i 的用户数

输出示例(来自上面的示例):

i_itemId  j_itemId  users(i)  users(j)  users(i,j)  users(i,~j)  users(~i, j)

200  200  3  3  3  0  0

200  300  3  2  1  2  1

300  300  2  2  2  0  0

300  200  3  2  1  1  2

注意

  1. 数据表很大 (11 GB),位于云端。我有一个可以使用的 SQL 框架。所以我无法下载文件并运行python(例如) 所以解决方案必须以高效的方式用 SQL 编写
  2. 解决方案不必是一条 SQL 语句。
  3. 我正在寻找一种有效的解决方案
  4. 我们可以假设这是一个关键
  5. 如果有人对这里的问题标题有更好的选择,我会很高兴更新它:)

【问题讨论】:

  • 能否请您对 i & J 的数据进行抽样以便快速查看。
  • 说明并使用您正在使用的 SQL 语言/环境的标签
  • 我举了一个小例子@RajatJaiswal
  • 如果用户不止一次购买了一件商品怎么办?这算作一次还是实际购买的次数?
  • 您使用的是哪个DBMS product? “SQL”只是一种查询语言,而不是特定数据库产品的名称。请为您正在使用的数据库产品添加标签postgresqloraclesql-serverdb2、...

标签: sql join count union intersection


【解决方案1】:

我不确定是否有一种“简单”的方法可以实现这一点。一种方法相当暴力:使用cross join 生成所有行。然后对每个单独的计数使用子查询:

select i1.itemid, i2.itemid, i1.num as cnt1, i2.num as cnt2,
       (select count(*)
        from t u1 join
             t u2
             on u1.userid = u2.userid
        where u1.itemid = i1.itemid and u2.itemid = i2.itemid
       ) as cnt_1_2,
       (select count(*)
        from t u1 left join
             t u2
             on u1.userid = u2.userid and u2.itemid = i2.itemid
        where u1.itemid = i1.itemid and u2.itemid is null
       ) as cnt_1_not2,
       (select count(*)
        from t u1 left join
             t u2
             on u1.userid = u2.userid and u1.itemid = i1.itemid
        where u2.itemid = i2.itemid and u1.itemid is null
       ) as cnt_not1_2
from (select itemid, count(*) as num from t group by itemid) i1 cross join
     (select itemid, count(*) as num from t group by itemid) i2;

【讨论】:

  • 感谢您在所有连接语句中强制使用 itemId == itemId。但我需要交叉连接来获取每个项目对的计算此外,它不必是一个 SQL 语句。这不会是有效的
  • @SamerAamar 。 . . from 子句执行cross join,所以我不明白你评论的那部分。使用正确的索引,单个查询应该相当有效。
【解决方案2】:

这是一个食谱

1) 创建一个临时表来收集 I 和 J 的总数。

免责声明:
此示例使用 MS SQL 服务器数据类型:INT。
因此,请将其更改为您的 RDBMS 支持的数字类型。
顺便说一句,在 MS SQL Server 中,临时表以 # 开头

create table TempTotals (iItemId int, jItemId int, TotalUsers int); 

2) 填写总计

delete from TempTotals;
insert into TempTotals (iItemId, jItemId, TotalUsers)
select 
    t1.ItemId as iItemId, 
    t2.ItemId as jItemId, 
    count(distinct t1.UserId) as TotalUsers
from YourTable t1
full join YourTable t2 on (t1.UserId = t2.UserId)
group by t1.ItemId, t2.ItemId;

3) 自连接临时表以获取所有总数

select 
 ij.iItemId, 
 ij.jItemId,
 i.TotalUsers as Users_I,
 j.TotalUsers as Users_J,
 ij.TotalUsers as Users_I_and_J, 
 (i.TotalUsers - ij.TotalUsers) as Users_I_no_J,
 (j.TotalUsers - ij.TotalUsers) as Users_J_no_I
from TempTotals ij
left join TempTotals i on (i.iItemId = ij.iItemId and i.iItemId = i.jItemId)
left join TempTotals j on (j.jItemId = ij.jItemId and j.iItemId = j.jItemId)

【讨论】:

    【解决方案3】:

    如果您使用的是 Oracle 数据库,则可以将嵌套表(集合)与多重集运算符进行比较。并获取具有基数的集合中元素的数量。

    所以你可以做的是:

    • 按 itemid 分组,将所有用户收集到一个嵌套表中
    • 将 this 的输出与自身交叉连接
    • 使用 multiset intersect/except 运算符根据需要获取集合中的元素数量

    有点像:

    create table t (
      ItemId int, UserId varchar2(10)
    );
    insert into t values (   200  ,  'user1');
    insert into t values (   200  ,  'user3');
    insert into t values (   200  ,  'user4');
    insert into t values (   300  ,  'user5');
    insert into t values (   300  ,  'user3');
    
    commit;
    
    create or replace type users_t as table of varchar2(10);
    /
    
    with grps as (
      select itemid, cast ( collect ( userid ) as users_t ) users
      from   t
      group  by itemid
    )
      select g1.itemid i, g2.itemid j,
             cardinality ( g1.users ) num_i,
             cardinality ( g2.users ) num_j,
             cardinality ( g1.users multiset intersect g2.users ) i_and_j,
             cardinality ( g1.users multiset except g2.users ) i_not_j,
             cardinality ( g2.users multiset except g1.users ) j_not_i
      from   grps g1
      cross  join grps g2;
    
    I     J     NUM_I   NUM_J   I_AND_J   I_NOT_J   J_NOT_I   
      200   200       3       3         3         0         0 
      200   300       3       2         1         2         1 
      300   200       2       3         1         1         2 
      300   300       2       2         2         0         0 
    

    如有必要,您可以通过在 i = j 时跳过除运算符来获得更高的性能,例如:

    case 
      when g1.itemid = g2.itemid then 0 
      else cardinality ( g1.users multiset intersect g2.users )
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      • 2020-12-04
      • 2015-02-11
      • 2021-01-30
      • 2018-03-20
      相关资源
      最近更新 更多