Crosstab with a large or undefined number of categories

后端 未结 3 502
野的像风
野的像风 2020-12-05 21:54

My real problem has to do with recording which of a very large number of anti-virus products agree that a given sample is a member of a given anti-virus family. The databas

3条回答
  •  春和景丽
    2020-12-05 22:32

    create table vote (Photo integer, Voter text, Decision text);
    insert into vote values
    (1, 'Alex', 'Cat'),
    (1, 'Bob', 'Dog'),
    (1, 'Carol', 'Cat'),
    (1, 'Dave', 'Cat'),
    (1, 'Ed', 'Cat'),
    (2, 'Alex', 'Cat'),
    (2, 'Bob', 'Dog'),
    (2, 'Carol', 'Cat'),
    (2, 'Dave', 'Cat'),
    (2, 'Ed', 'Dog'),
    (3, 'Alex', 'Horse'),
    (3, 'Bob', 'Horse'),
    (3, 'Carol', 'Dog'),
    (3, 'Dave', 'Horse'),
    (3, 'Ed', 'Horse'),
    (4, 'Alex', 'Horse'),
    (4, 'Bob', 'Horse'),
    (4, 'Carol', 'Cat'),
    (4, 'Dave', 'Horse'),
    (4, 'Ed', 'Horse'),
    (5, 'Alex', 'Dog'),
    (5, 'Bob', 'Cat'),
    (5, 'Carol', 'Cat'),
    (5, 'Dave', 'Cat'),
    (5, 'Ed', 'Cat')
    ;
    

    The query for the cat:

    select photo,
        alex + bob + carol + dave + ed as Total,
        alex, bob, carol, dave, ed
    from crosstab($$
        select
            photo, voter,
            case decision when 'Cat' then 1 else 0 end
        from vote
        order by photo
        $$,'
        select distinct voter
        from vote
        order by voter
        '
    ) as (
        photo integer,
        Alex integer,
        Bob integer,
        Carol integer,
        Dave integer,
        Ed integer
    );
     photo | total | alex | bob | carol | dave | ed 
    -------+-------+------+-----+-------+------+----
         1 |     4 |    1 |   0 |     1 |    1 |  1
         2 |     3 |    1 |   0 |     1 |    1 |  0
         3 |     0 |    0 |   0 |     0 |    0 |  0
         4 |     1 |    0 |   0 |     1 |    0 |  0
         5 |     4 |    0 |   1 |     1 |    1 |  1
    

    If the number of voters is big or not known then it can be done dynamically:

    do $do$
    declare
    voter_list text;
    r record;
    begin
    
    drop table if exists pivot;
    
    voter_list := (
        select string_agg(distinct voter, ' ' order by voter) from vote
        );
    
    execute(format('
        create table pivot (
            decision text,
            photo integer,
            Total integer,
            %1$s
        )', (replace(voter_list, ' ', ' integer, ') || ' integer')
    ));
    
    for r in
    select distinct decision from vote
    loop
        execute (format($f$
            insert into pivot
            select
                %3$L as decision,
                photo,
                %1$s as Total,
                %2$s
            from crosstab($ct$
                select
                    photo, voter,
                    case decision when %3$L then 1 else 0 end
                from vote
                order by photo
                $ct$,$ct$
                select distinct voter
                from vote
                order by voter
                $ct$
            ) as (
                photo integer,
                %4$s
            );$f$,
            replace(voter_list, ' ', ' + '),
            replace(voter_list, ' ', ', '),
            r.decision,
            replace(voter_list, ' ', ' integer, ') || ' integer'
        ));
    end loop;
    end; $do$;
    

    The above code created the table pivot with all the decisions:

    select * from pivot where decision = 'Cat';
    

提交回复
热议问题