SQL查询,关联给定消费者的每个集合集的数量

时间:2018-05-09 19:24:19

标签: mysql sql mariadb

说我有客户可以获得某些奖项:

SELECT gs.claimed_by AS consumer_id, p.prize_id AS prize_id FROM 
  awarded_prizes

现在,客户1有三个奖项,而客户2有一个奖项

+-------------+----------+
| consumer_id | prize_id |
+-------------+----------+
|           1 |       45 |
|           1 |       46 |
|           1 |       47 |
|           2 |       66 |
+-------------+----------+

假设我们也有收藏品,如果您将所有成员收集到该收藏品中,那么您现在拥有一个收藏品集:

SELECT set_id, member_prize_id AS prize_id FROM collectable_set_members;
+--------+----------+
| set_id | prize_id |
+--------+----------+
|      1 |       45 |
|      1 |       46 |
|      1 |       47 |
|      2 |       65 |
|      2 |       66 |
+--------+----------+

通过上表和上一个查询,我们可以看到客户1已完成设置1(他们有45,46,47),而客户2没有完成任何操作。

有些情况下,客户可以多次完成一组(客户可以在award_prize表中拥有45,46,47,45,46,47。

我一直在关注pantry problem及其变体(如酒保问题),一直在玩交叉连接和分组,似乎无法找到我想要的东西。

我试图获得给定客户的结果,显示他们拥有的所有set_id以及他们已完成的套数:

+-------------+---------------+--------+
| consumer_id | completed_set |  count |
+-------------+---------------+--------+
|           1 |             1 |      1 |
+-------------+---------------+--------+ 

我在mariadb:5.5

3 个答案:

答案 0 :(得分:1)

请参阅此处SqlFiddle

我的桌子名称与你的不同,但它证明了这一点:

select sets_x_consumers.consumer_id, sets_x_consumers.set_id, 
  set_summary.items_in_set = consumer_summary.items_per_set_per_consumer as set_is_complete
from (
  -- build a cross-product of sets and consumers
  select distinct set_id, consumer_id
  from sets join consumers -- no join condition -> cross product
) sets_x_consumers 
inner join
( -- the total number of items in each set per set_id
  select set_id, count(*) items_in_set
  from sets 
  group by set_id
) set_summary on sets_x_consumers.set_id = set_summary.set_id
inner join
( -- the total number of items per set and customer 
  select set_id, consumer_id, count(*) items_per_set_per_consumer
  from sets 
  inner join consumers on sets.prize_id = consumers.prize_id
  group by consumer_id, set_id
) consumer_summary on sets_x_consumers.set_id = consumer_summary.set_id and sets_x_consumers.consumer_id = consumer_summary.set_id

我的基本想法是总结每个集合中的项目数量以及每个消费者声称的每个集合的项目数量。只要这对消费者和奖品没有重复的条目,这应该有效(如果允许重复,我会count distinct(prize_id)使用consumer_summary

上面查询的输出是:

| consumer_id | set_id | set_is_complete |
|-------------|--------|-----------------|
|           1 |      1 |               1 |
|           2 |      2 |               0 |

这列出了每对消费者和消费者至少有一个奖品。 (要更改此列表以列出每个消费者组合,请使用outer join

在此基础上仅列出完整集或总结完整集的数量应该很容易; - )

答案 1 :(得分:1)

无法弄清楚你的最后一栏“计数”是什么意思, 但这里有一个解决方案,列出用户及其设置已完成。

demo Link

整个想法是计算每组所需的奖品数量,并计算每个客户每套收集的奖品,因此您可以加入这两个奖品。

我知道它是mssql,但我没有设法让mysql ctes在sqfiddle中工作。 CTE-s基本上只是一个子查询。如果您的服务器不支持CTE-s,您可以使用普通的子查询或临时表。

答案 2 :(得分:0)

为了它的价值,我在Sql Server中为此提出了一个很好的例程。即使每组中都有重叠的prize_id值,这也会有效(如果不明确,则默认为更高的setid)。假设所有临时表都是原始数据:

declare @awarded_prize table (rowid int identity, consumer_id int, prize_id int )
insert @awarded_prize
select * from #awarded_prizes

declare @collections table ( set_id int, prize_id int, rownumber int , filled int)
insert @collections
select *, row_number() over(partition by set_id order by set_id, prize_id) , null
from #collections

declare @todelete table (rowid int)
declare @scorecard table (consumer_id int, set_id int)

declare @iterator int=1
declare @prize_id int
declare @set_id int = (Select min(set_id) from @collections)
declare @consumer_id int = (Select min(consumer_id) from @awarded_prize)
while @consumer_id<=(select max(consumer_id) from @awarded_prize)
    begin
    while @set_id<=(select max(set_id) from @collections)
      begin
        while 1=1
        begin
        select @prize_id=prize_id 
        from @collections 
        where set_id=@set_id and rownumber=@iterator


        if (select max(rowid) from @awarded_prize where prize_id=@prize_id and consumer_id=@consumer_id and rowid not in (select rowid from @todelete)) is null break
        insert @todelete
        select max(rowid) from @awarded_prize where prize_id=@prize_id and consumer_id=@consumer_id and rowid not in (select rowid from @todelete)

        update @collections set filled=1 
        where rownumber=@iterator and set_id=@set_id

        if not exists(select 1 from @collections where set_id=@set_id and filled is null)
        begin
            insert @scorecard
            select @consumer_id, @set_id
            delete @awarded_prize where rowid in (Select rowid from @todelete) 
            delete @todelete
            update @collections set filled=null where filled=1
        end
        set @iterator=case when @iterator=(Select max(rownumber) from @collections where set_id=@set_id) then 
        (select min(rownumber) from @collections where set_id=@set_id) else @iterator+1 end 
      end
      delete @todelete
      set @iterator=1
      set @set_id=@set_id+1
    end
    set @iterator=1
    select @set_id=min(set_id) from @collections
    select @consumer_id=min(consumer_id) from @awarded_prize where consumer_id>@consumer_id
    end

    select consumer_id, set_id, count(*) complete_sets 
    from @scorecard
    group by consumer_id, set_id
    order by consumer_id, set_id