sql - 具有大量或未定义类别的交叉表

标签 sql postgresql pivot aggregate crosstab

我真正的问题是记录大量反病毒产品中的哪些同意给定样本是给定反病毒家族的成员。该数据库有数百万个样本,每个样本都有数十种反病毒产品投票。我想问这样的查询:“对于包含名称 'XYZ' 的恶意软件,哪个样本得票最多,哪些供应商投票支持它?”并得到如下结果:

"BadBadVirus"  
                     V1  V2  V3  V4  V5  V6  V7  
Sample 1 - 4 votes    1   0   1   0   0   1   1      
Sample 2 - 5 votes    1   0   1   0   1   1   1   
Sample 3 - 5 votes    1   0   1   0   1   1   1  

 total     14         3       3       2   3   3  

这可能用来告诉我供应商 2 和供应商 4 要么不知道如何 检测此恶意软件,或者他们将其命名为不同的名称。


我将尝试稍微概括一下我的问题,同时希望不会破坏您帮助我的能力。假设我有五个选民(Alex、Bob、Carol、Dave、Ed),他们被要求查看五张照片(P1、P2、P3、P4、P5)并决定照片的“主要主题”是什么。对于我们的示例,我们假设它们仅限于“猫”、“狗”或“马”。并非每个选民都对每件事都投票。

数据以这种形式存在于数据库中:

Photo, Voter, Decision
(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')

目标是给定一个我们正在寻找的照片主题,我们想知道有多少投票者认为这是该照片的主要观点,但也列出了哪些投票者这样认为。

Query for: "Cat"
      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
------------------------------------
total  12      2    1    4     3   2 

Query for: "Dog"
      Total  Alex  Bob Carol Dave Ed
1 -     1     0      1   0    0    0
2 -     2     0      1   0    0    1
3 -     1     0      0   1    0    0 
4 -     0     0      0   0    0    0 
5 -     1     1      0   0    0    0 
------------------------------------
total   5     1      2   1    0    1 

我可以用我存储的格式处理数据吗?

我很难获得执行此操作的查询 - 虽然转储数据然后编写程序来执行此操作很简单,但如果可以的话,我真的希望能够在数据库中执行此操作.

感谢您的任何建议。

最佳答案

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')
;

猫的查询:

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

如果投票者的数量很大或未知,则可以动态完成:

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$;

上面的代码创建了包含所有决策的表数据透视表:

select * from pivot where decision = 'Cat';

关于sql - 具有大量或未定义类别的交叉表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12988575/

相关文章:

python - 如何 reshape (pivot_wider和pivot_longer)pandas DataFrame

mysql - 在 mySQL 中创建动态表

sql - 插入 2 行,每行插入不同的表,其中一行引用作者的主键

Mysql提取json数据并搜索多个值

c# - SQL Server 2008 从大于给定日期时间的返回值中选择最小值

SQL 返回一个不同的列和不同列的第一个日期

postgresql - 如何更改 Elastic Beanstalk 的 RDS 实例

postgresql - 所有表的删除触发器

javascript - 使用 javascript 在 html 中添加动态行

mysql - 如何列出员工每周是否记录时间