Oracle/SQL - Using the rank function
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,
精彩评论