1

I have a MySQL data set of credit card transactions.

create table trans (
  card_id int,
  amount int
);

insert into trans values (1, 1);
insert into trans values (2, 1);
insert into trans values (3, 1);
insert into trans values (4, 1);
insert into trans values (5, 1);
insert into trans values (5, 1);
insert into trans values (6, 1);
insert into trans values (6, 1);
insert into trans values (7, 1);
insert into trans values (7, 1);
insert into trans values (8, 1);
insert into trans values (8, 1);
insert into trans values (8, 1);
insert into trans values (9, 1);
insert into trans values (9, 1);
insert into trans values (9, 1);
insert into trans values (10, 1);
insert into trans values (10, 1);
insert into trans values (10, 1);
insert into trans values (10, 1);

I desire to know:

1. how many cards were used to make at least 1 transaction
2. how many cards were used to make at least 5 transactions
3. how many cards were used to make at least 10 transactions
4. how many cards were used to make at least 20 transactions
etc...

Because the groups overlap, it appears conditional aggregation is a better approach:

select sum(cnt >= 1) as trans_1,
       sum(cnt >= 5) as trans_5,
       sum(cnt >= 10) as trans_10,
       sum(cnt >= 20) as trans_20
from (select card_id, count(*) as cnt
      from trans
      group by card_id 
      ) d;

The problem is the above produces a result set in columns, but I am trying to produce a result set in rows.

The traditional way in MySQL to pivot columns to rows is to use a sequence of select sum() from table union repetitively, but in this case the base data is a derived table, so that method seems to not work here. Any ideas of how to flip the columns to rows?

http://sqlfiddle.com/#!9/0f741/3

davidjhp
  • 7,816
  • 9
  • 36
  • 56

2 Answers2

2

You can do this either by pivoting the rows after the calculation or by doing the calculation on separate rows. The first should have much better performance:

select x.which,
       (case when x.n = 1 then trans_1
             when x.n = 2 then trans_5
             when x.n = 3 then trans_10
             when x.n = 4 then trans_20
        end) as numtransactions
from (select sum(cnt >= 1) as trans_1,
             sum(cnt >= 5) as trans_5,
             sum(cnt >= 10) as trans_10,
             sum(cnt >= 20) as trans_20
      from (select card_id, count(*) as cnt
            from trans
            group by card_id 
           ) d
     ) d join
     (select 1 as n, '1 or more' as which union all
      select 2, '5 or more' union all
      select 3, '10 or more' union all
      select 4, '20 or more'
     ) x
order by x.n;
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
0

This is a simple answer, but it works. It's somewhat less optimal in that the query needs to be executed multiple times to do the grouping and counting.

select sum(cnt >= 1) as trans_1
from (select card_id, count(*) as cnt
      from trans
      group by card_id 
      ) d
UNION ALL
select sum(cnt >= 2) as trans_2
from (select card_id, count(*) as cnt
      from trans
      group by card_id 
      ) d
UNION ALL
select sum(cnt >= 3) as trans_10
from (select card_id, count(*) as cnt
      from trans
      group by card_id 
      ) d
UNION ALL
select sum(cnt >= 4) as trans_20
from (select card_id, count(*) as cnt
      from trans
      group by card_id 
      ) d;
Vinbot
  • 518
  • 2
  • 14