2016-06-30 10 views
2

У меня проблема с SQL-запросом, это можно сделать в «обычном» SQL, но, поскольку я уверен, что мне нужно использовать некоторую группу concatenation (не может использовать MySQL), поэтому второй вариант - диалоги ORACLE, так как будет база данных Oracle. Скажем, у нас есть следующие объекты:Oracle ListaGG, Top 3 самых частых значения, заданных в одном столбце, сгруппированных по ID

Таблица: Ветеринарные посещения

Visit_Id, 
Animal_id, 
Veterinarian_id, 
Sickness_code 

Допустим есть 100 посещений (100 visit_id) и каждый animal_id посещение около 20 раз.

Мне нужно создать SELECT, сгруппированных по Animal_id с 3-мя колоннами

  • animal_id
  • второй показывает агрегированные суммы посещений гриппа для этого конкретного животного (скажем, грипп, sickness_code = 5)
  • 3-я колонка показывает три верхних кодов болезни для каждого животного (верхние 3 чаще всего кодов для данного конкретного животного_ид)

Как это сделать? Первый и второй столбцы просты, но в-третьих? Я знаю, что мне нужно использовать LISTAGG из Oracle, OVER PARTITION BY, COUNT и RANK, я пытался связать это вместе, но не получилось так, как я ожидал :(Как должен выглядеть такой запрос?

+0

Здравствуйте! Добро пожаловать в StackOverflow! Не могли бы вы разместить небольшой набор выборочных данных, а также пример того, что вы хотите вывести? – dvsoukup

+0

Пожалуйста, прочитайте [** How-to-Ask **] (http://stackoverflow.com/help/how-to-ask) \t \t И вот отличное место для [** START **] (Http: // spaghettidba.ru/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum /), чтобы узнать, как улучшить качество вопроса и получить лучшие ответы. –

+0

[** Как создать минимальный, полный и проверенный пример **] (http://stackoverflow.com/help/mcve) Например, вместо 100 посещения используйте 10. два животного, и каждый получает 5 посещений. и показать результат с результатом TOP 2. –

ответ

1

Я думаю, что наиболее естественный способ использует два уровня агрегации, наряду с тире функций окна здесь и там:

select vas.animal, 
     sum(case when sickness_code = 5 then cnt else 0 end) as numflu, 
     listagg(case when seqnum <= 3 then sickness_code end, ',') within group (order by seqnum) as top3sicknesses 
from (select animal, sickness_code, count(*) as cnt, 
      row_number() over (partition by animal order by count(*) desc) as seqnum 
     from visits 
     group by animal, sickness_code 
    ) vas 
group by vas.animal; 

Это использует тот факт, что listagg() игнорирует NULL значения

+0

Элегантное решение с игнорированием NULL и сохранением подзапросов (+1). Pls исправить 'порядок по счету (*)' в 'row_number'. ('cnt' не определен) –

+0

@MarmiteBomber. , , Спасибо. –

0

здесь выборочные данные

create table VET as 
select 
rownum+1 Visit_Id, 
mod(rownum+1,5) Animal_id, 
cast(NULL as number) Veterinarian_id, 
trunc(10*dbms_random.value)+1 Sickness_code 
from dual 
connect by level <=100; 
.

Запрос

в основном подзапросы сделать следующее:

совокупный количество и рассчитать количество гриппа (во всех записях животного)

рассчитайте RANK (если вам нужно действительно только 3 записи с помощью ROW_NUMBER - см обсуждение ниже)

фильтр верх 3 Звания

результат LISTAGGregate

with agg as (
select Animal_id, Sickness_code, count(*) cnt, 
sum(case when SICKNESS_CODE = 5 then 1 else 0 end) over (partition by animal_id) as cnt_flu 
from vet 
group by Animal_id, Sickness_code 
), agg2 as (
select ANIMAL_ID, SICKNESS_CODE, CNT, cnt_flu, 
rank() OVER (PARTITION BY ANIMAL_ID ORDER BY cnt DESC) rnk 
from agg 
), agg3 as (
select ANIMAL_ID, SICKNESS_CODE, CNT, CNT_FLU, RNK 
from agg2 
where rnk <= 3 
) 
select 
ANIMAL_ID, max(CNT_FLU) CNT_FLU, 
LISTAGG(SICKNESS_CODE||'('||CNT||')', ', ') WITHIN GROUP (ORDER BY rnk) as cnt_lts 
from agg3 
group by ANIMAL_ID 
order by 1; 

дает

ANIMAL_ID CNT_FLU CNT_LTS          
---------- ---------- --------------------------------------------- 
     0   1 6(5), 1(4), 9(3)        
     1   1 1(5), 3(4), 2(3), 8(3)       
     2   0 1(5), 10(3), 4(3), 6(3), 7(3)     
     3   1 5(4), 2(3), 4(3), 7(3)       
     4   1 2(5), 10(4), 1(2), 3(2), 5(2), 7(2), 8(2) 

Я намеренно показывают Sickness_code (количество посещений) в demonstarte, что топ 3 может иметь связи, которые вы должны справиться. Проверьте функцию RANK. В этом случае использование ROW_NUMBER не является детерминированным.