Голосование большинства столбцов SQL - PullRequest
0 голосов
/ 12 января 2019

Мне нужно сделать что-то вроде «голосования большинством» столбцов в базе данных SQL. Это означает, что имея столбцы: c0, c1, ..., cn, я хотел бы иметь в каком-то другом столбце для каждой строки самое частое значение среди упомянутых столбцов (и null или случайное в противном случае - это на самом деле не имеет значения). Например, если у нас есть следующая таблица:

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

Это то, что я имею в виду под большинством голосов столбцов c0, c1, c2: в первой строке у нас есть 2 строки со значением 0 и 1 со 1, поэтому result = 0. Во втором у нас есть один 0 против двух 1, ergo result = 1 и так далее. Мы предполагаем, что все столбцы имеют одинаковый тип.

Было бы замечательно, если бы запрос был кратким (его можно построить динамически). Нативный SQL предпочтителен, но PL / SQL, psql также подойдет.

Заранее спасибо.

Ответы [ 4 ]

0 голосов
/ 12 января 2019

В Postgres используются функции jsonb. Вам нужен первичный ключ или уникальный столбец (столбцы), 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)

Запрос работает хорошо независимо от количества столбцов.

0 голосов
/ 12 января 2019

ЭТО ОТВЕТЫ НА ОРИГИНАЛЬНУЮ ВЕРСИЮ ВОПРОСА.

Вы можете просто сравнить значения. Для вашего примера с двумя значениями, ни одно из которых не равно 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;
0 голосов
/ 12 января 2019

Это легко сделать, создав таблицу из трех столбцов и используя для этого статистическую функцию:

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

Если вы не хотите жестко кодировать имена столбцов, вы также можете использовать JSON-функцию Postgres:

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;
0 голосов
/ 12 января 2019

Вот решение для 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;

дб <> скрипка

В первом подзапросе все значения с максимальным числом берутся с использованием rank(). Оконная версия count() затем используется для фильтрации, если существует только одно значение с максимальным количеством.

Если вам нужно сделать это для нескольких столбцов, просто добавьте их к SELECT и VALUES.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...