Oracle / SQL - Использование функции ранга - PullRequest
0 голосов
/ 13 сентября 2011

То, что я пытаюсь сделать, это список людей из таблицы, и в случае, если человек существует более одного раза, затем вернуть обратно свою запись, которая содержит «код» с наивысшим рейтингом

Ранжирование кодов (от высокого к низкому): T, E, F

Так что для данного набора данных

Person  Code
----------------    
Tom     F
Paul    E
Mark    F
Paul    T
Mark    E
Chris   T
Chris   E

Я бы получил следующее от моего запроса

Person  Code
----------------
Tom     F
Paul    T
Mark    E
Chris   T

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

Спасибо!

Ответы [ 4 ]

2 голосов
/ 13 сентября 2011

Вы можете использовать функцию RANK для ранжирования данных

SQL> ed
Wrote file afiedt.buf

  1  with data as (
  2    select 'Tom' person, 'F' code from dual union all
  3    select 'Paul', 'E' from dual union all
  4    select 'Paul', 'T' from dual union all
  5    select 'Mark', 'F' from dual union all
  6    select 'Mark', 'E' from dual
  7  )
  8  select *
  9    from (select person,
 10                 code,
 11                 rank() over (partition by person
 12                                  order by (case when code='T' then 1
 13                                                 when code='E' then 2
 14                                                 when code='F' then 3
 15                                                 else null
 16                                             end)) rnk
 17*           from data)
SQL> /

PERS C        RNK
---- - ----------
Mark E          1
Mark F          2
Paul T          1
Paul E          2
Tom  F          1

Elapsed: 00:00:00.00

Затем вам просто нужно выбрать строки с RNK, равным 1

SQL> ed
Wrote file afiedt.buf

  1  with data as (
  2    select 'Tom' person, 'F' code from dual union all
  3    select 'Paul', 'E' from dual union all
  4    select 'Paul', 'T' from dual union all
  5    select 'Mark', 'F' from dual union all
  6    select 'Mark', 'E' from dual
  7  )
  8  select *
  9    from (select person,
 10                 code,
 11                 rank() over (partition by person
 12                                  order by (case when code='T' then 1
 13                                                 when code='E' then 2
 14                                                 when code='F' then 3
 15                                                 else null
 16                                             end)) rnk
 17            from data)
 18*  where rnk = 1
SQL> /

PERS C        RNK
---- - ----------
Mark E          1
Paul T          1
Tom  F          1

Elapsed: 00:00:00.00
0 голосов
/ 13 сентября 2011

Самое короткое и наиболее эффективное и специфичное для Oracle решение:

SQL> create table mytable(person,code)
  2  as
  3  select 'Tom', 'F' from dual union all
  4  select 'Paul', 'E' from dual union all
  5  select 'Mark', 'F' from dual union all
  6  select 'Paul', 'T' from dual union all
  7  select 'Mark', 'E' from dual union all
  8  select 'Chris', 'T' from dual union all
  9  select 'Chris', 'E' from dual
 10  /

Table created.

SQL> select person
  2       , max(code) keep (dense_rank first order by decode(code,'T',1,'E',2,'F',3,4)) code
  3    from mytable
  4   group by person
  5  /

PERSO C
----- -
Chris T
Mark  E
Paul  T
Tom   F

4 rows selected.

С уважением,
Роб.

0 голосов
/ 13 сентября 2011

Гул ... Альтернативное предложение со стандартным SQL. Имейте CODE_WEIGHT таблицу, такую ​​как:

CODE WEIGHT
T    3    
E    2
F    1

Затем сгруппируйте запрос по Person (если это критерий группировки) и выберите отдельный код, содержащий max(weight).

Я отправлю запрос через пару минут.

UPDATE

Хорошо, извините за задержку.

Вот решение с использованием предыдущей указанной таблицы и трюка @Randy:

SELECT 
 pp.person, decode(max(c.weight), 3, 'T', 2, 'E', 1, 'F', '') code
FROM 
 person pp INNER JOIN code_weight c on (pp.code = c.code)
GROUP BY
 pp.person 
ORDER BY 
 person DESC;

Я почти уверен, что есть способ избавиться от проприетарной функции Oracle и добиться цели в чистом SQL ... Во всяком случае, поскольку вы запросили решение Oracle, вот оно.

ОБНОВЛЕНИЕ 2

И, как и было обещано, вот лучшая стандартная версия SQL, которую я смог придумать:

SELECT 
 p.person, c.code
FROM
(
 SELECT 
   pp.person, MAX(cc.weight) weight
 FROM 
  person pp INNER JOIN code_weight cc ON (pp.code = cc.code)
 GROUP BY
  pp.person 
) p INNER JOIN code_WEIGHT c ON (p.weight = c.weight)
ORDER BY
  p.person DESC;

Как-то безобразно с двумя соединениями ... Но это делает работу без проприетарных расширений. Любой гуру SQL знает, как его оптимизировать?

Приветствия

0 голосов
/ 13 сентября 2011

я не думаю, что RANK - это то, что тебе нужно ...

в принципе, ваше удаление будет выглядеть так: (псевдопросмотр)

delete the rows from person
where that row is not in ( select the rows from person with the highest code )

редактировать

этот трюк тоже может вам помочь:

select person, code, decode( code, 'T', 1, 'E', 2, 'F', 3, 0 ) from mytable
...