开发者

Oracle/SQL - Using the rank function

开发者 https://www.devze.com 2023-04-04 04:33 出处:网络
What I\'m tryi开发者_如何学Pythonng to do is a list of persons from a table and in the event where a person exists more than once then return back their record that contains the highest ranked \'code\

What I'm tryi开发者_如何学Pythonng to do is a list of persons from a table and in the event where a person exists more than once then return back their record that contains the highest ranked 'code'

Code ranking (high to low): T, E, F

So for the given dataset

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

I would get the following back from my query

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

I'm assuming this is going to use the rank/analytic functions, but I'm just not familiar enough with them.

Thanks!


You can use the RANK function to rank the data

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

Then, you just need to select the rows with a RNK of 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


The shortest and most performant and Oracle specific solution:

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.

Regards,
Rob.


i don't think RANK is what you need...

basically, your delete will look like this: (pseudo-query)

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

edit:

this trick might help you too:

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


Hum... Alternative suggestion with standard SQL. Have a CODE_WEIGHT table such as:

CODE WEIGHT
T    3    
E    2
F    1

Then group your query by Person (if this is the grouping criterion) and select the distinct code containing max(weight).

I'll post the query in a couple of minutes.

UPDATE

Ok, sorry for the delay.

Here is a solution using the previous stated table and @Randy trick:

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;

I'm pretty sure there is a way to dump Oracle proprietary function and get things done in pure SQL... Anyway, since you've asked for a Oracle solution, here it is.

UPDATE 2

And as promised, here's the best standard SQL version that I was able to come up with:

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;

Kinda ugly with the two joins... But it does the job without proprietary extensions. Any SQL guru knows how to optimize it?

Cheers,

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号