列的多数表决SQL

时间:2019-01-12 18:35:34

标签: sql oracle postgresql

我需要对SQL数据库中的列进行“多数投票”。这就是说,有c0c1,...,cn列,我想在其他各列中为每行提供上述列中最频繁的值(和null或其他随机变量-并不重要)。例如,如果我们有下表:

+--+--+--+------+
|c0|c1|c2|result|
+--+--+--+------+
| 0| 1| 0|     0|
| 0| 1| 1|     1|
| 2| 2| 0|     2|
| 0| 3| 1|  null|

这就是我对c0c1c2列进行多数表决的意思:在第一行中,我们有2行,其值分别为0和1行,其{ {1}},因此1。在第二个中,我们有一个result = 0和两个0,因此是1,依此类推。我们假设所有列都具有相同的类型。

如果查询简洁(可以动态构建),那就太好了。首选本机SQL,但是PL / SQL,psql也可以。

谢谢。

4 个答案:

答案 0 :(得分:1)

这是Postgres的解决方案。

SELECT t1.c0,
       t1.c1,
       t1.c2,
       (SELECT y.c
               FROM (SELECT x.c,
                            count(*) OVER (PARTITION BY x.rn) ct
                            FROM (SELECT v.c,
                                         rank() OVER (ORDER BY count(v.c) DESC) rn
                                         FROM (VALUES (t1.c0),
                                                      (t1.c1),
                                                      (t1.c2)) v(c)
                                         GROUP BY v.c) x
                            WHERE x.rn = 1) y
               WHERE y.ct = 1) result
       FROM elbat t1;

db<>fiddle

在子查询中,首先使用rank()获取所有最大计数值。然后,count()的窗口版本将用于过滤只有最大计数的一个值。

如果需要在更多列上执行此操作,只需将它们添加到SELECTVALUES中。

答案 1 :(得分:1)

可以轻松地通过在三列中创建一个表并在其上使用聚合函数来完成此操作:

以下在Postgres中起作用:

select c0,c1,c2,
       (select c 
       from unnest(array[c0,c1,c2]) as t(c) 
       group by c 
       having count(*) > 1 
       order by count(*) desc 
       limit 1)
from the_table;

如果您不想对列名进行硬编码,则也可以使用Postgres的JSON函数:

select t.*,
       (select t.v
        from jsonb_each_text(to_jsonb(t)) as t(c,v)
        group by t.v
        having count(*) > 1
        order by count(*) desc
        limit 1) as result
from the_table t;

请注意,以上内容考虑了所有列。如果要删除特定的列(例如id列),则需要使用to_jsonb(t) - 'id'从JSON值中删除该键。

这些解决方案都不涉及联系(两个不同的值出现相同的次数)。

在线示例:https://rextester.com/PJR58760


第一个解决方案可以在某种程度上“适应” Oracle,特别是如果您可以动态构建SQL:

select t.*, 
       (select c
        from (
          -- this part would need to be done dynamically
          -- if you don't know the columns
          select t.c0 as c from dual union all 
          select t.c1 from dual union all 
          select t.c2 from dual
        ) x
        group by c
        having count(*) > 1
        order by count(*) desc
        fetch first 1 rows only) as result
from the_table t;

答案 2 :(得分:1)

在Postgres中使用jsonb functions.,您需要主键或唯一列,在示例中,id是唯一的:

with my_table(id, c0, c1, c2) as (
values
    (1, 0, 1, 0),
    (2, 0, 1, 1),
    (3, 2, 2, 0),
    (4, 0, 3, 1)
)

select distinct on (id) id, value
from (
    select id, value, count(*)
    from my_table t
    cross join jsonb_each_text(to_jsonb(t)- 'id')
    group by id, value
    ) s
order by id, count desc

 id | value 
----+-------
  1 | 0
  2 | 1
  3 | 2
  4 | 1
(4 rows)

无论列数如何,查询都能很好地运行。

答案 3 :(得分:0)

这回答了问题的原始版本。

您可以比较这些值。对于您的示例,有两个值都不是NULL

select t.*
       (case when ((case when c0 = 0 then 1 else -1 end) +
                   (case when c1 = 0 then 1 else -1 end) +
                   (case when c2 = 0 then 1 else -1 end)
                  ) > 0
             then 0 else 1
        end)
from t;