I have the following setup, which is working perfectly. I am difficulty figuring out the syntax how to display the course name in the output. In my test CASE all the rows should have the value Geometry.
In addition, how could I use rank or rank_dense to limit the output to display only 1 row with the highest average?
CREATE TABLE students(student_id, first_name, last_name) AS
SELECT 1, 'Faith', 'Aaron' FROM dual UNION ALL
SELECT 2, 'Lisa', 'Saladino' FROM dual UNION ALL
SELECT 3, 'Leslee', 'Altman' FROM dual UNION ALL
SELECT 4, 'Patty', 'Kern' FROM dual UNION ALL
SELECT 5, 'Betty', 'Bowers' FROM dual;
CREATE TABLE courses(course_id, course_name) AS
SELECT 1, 'Geometry' FROM dual UNION ALL
SELECT 2, 'Trigonometry' FROM dual UNION ALL
SELECT 3, 'Calculus' FROM DUAL;
CREATE TABLE grades(student_id,
course_id, grade) AS
SELECT 1, 1, 75 FROM dual UNION ALL
SELECT 1, 1, 81 FROM dual UNION ALL
SELECT 1, 1, 76 FROM dual UNION ALL
SELECT 2, 1, 100 FROM dual UNION ALL
SELECT 2, 1, 95 FROM dual UNION ALL
SELECT 2, 1, 96 FROM dual UNION ALL
SELECT 3, 1, 80 FROM dual UNION ALL
SELECT 3, 1, 85 FROM dual UNION ALL
SELECT 3, 1, 86 FROM dual UNION ALL
SELECT 4, 1, 88 FROM dual UNION ALL
SELECT 4, 1, 85 FROM dual UNION ALL
SELECT 4, 1, 91 FROM dual UNION ALL
SELECT 5, 1, 98 FROM dual UNION ALL
SELECT 5, 1, 74 FROM dual UNION ALL
SELECT 5, 1, 81 FROM dual;
/* average grade of each student */
select s.student_id
, s.first_name
, s.last_name
, round(avg(g.grade), 1) as student_avg
from students s
join grades g
on s.student_id = g.student_id
group by s.student_id, s.first_name, s.last_name
ORDER BY avg(g.grade) DESC;
Something like this?
SQL> with temp as
2 (select s.student_id
3 , s.first_name
4 , s.last_name
5 , c.course_name
6 , round(avg(g.grade), 1) as student_avg
7 , rank() over (order by avg(g.grade) desc) rnk
8 from students s join grades g on s.student_id = g.student_id
9 join courses c on c.course_id = g.course_id
10 group by s.student_id, s.first_name, s.last_name, c.course_name
11 )
12 select student_id, first_name, last_name, course_name, student_avg
13 from temp
14 where rnk <= 3
15 order by rnk;
STUDENT_ID FIRST_ LAST_NAM COURSE_NAME STUDENT_AVG
---------- ------ -------- ------------ -----------
2 Lisa Saladino Geometry 97
4 Patty Kern Geometry 88
5 Betty Bowers Geometry 84.3
SQL>
Related
I have two tables User_details and Level_details.
User_details table:
ID Name
1 A
2 B
3 C
4 D
5 E
Level_details table:
trns_id Lvl usr_id
66 1 1
66 1 5
77 1 2
77 2 3
66 2 4
66 2 3
77 2 3
66 2 4
I am getting the result like:
trns_id Lvl name
66 1 A, E
66 2 D, C, D
77 1 B
77 2 C, C
I am using LISTAG function to get name
LISTAGG(( SELECT name FROM User_details l WHERE l.usr_id = id and trns_id=t1.trns_id and lvl=t1.lvl ), ',') WITHIN GROUP( ORDER BY lvl ) AS Name
You can use the distinct modifier in a listagg function call:
SELECT trns_id, lvl, LISTAGG(DISTINCT name, ', ') WITHIN GROUP (ORDER BY name)
FROM level_details l
JOIN user_details u ON l.usr_id = u.id
GROUP BY trns_id, lvl
If your database version doesn't support DISTINCT within LISTAGG, then you'll have to first select distinct values (lines #21 - 23), then aggregate them (line #20). Lines #1 - 17 represent sample data; you already have that and don't type it. Query you need begins at line #18.
SQL> with user_details (usr_id, name) as
2 (select 1, 'A' from dual union all
3 select 2, 'B' from dual union all
4 select 3, 'C' from dual union all
5 select 4, 'D' from dual union all
6 select 5, 'E' from dual
7 ),
8 level_details (trns_id, lvl, usr_id) as
9 (select 66, 1, 1 from dual union all
10 select 66, 1, 5 from dual union all
11 select 77, 1, 2 from dual union all
12 select 77, 2, 3 from dual union all
13 select 66, 2, 4 from dual union all
14 select 66, 2, 3 from dual union all
15 select 77, 2, 3 from dual union all
16 select 66, 2, 4 from dual
17 )
18 select x.trns_id,
19 x.lvl,
20 listagg(x.name, ', ') within group (order by x.lvl) name
21 from (select distinct u.usr_id, u.name, d.trns_id, d.lvl
22 from user_details u join level_details d on d.usr_id = u.usr_id
23 ) x
24 group by x.trns_id,
25 x.lvl;
TRNS_ID LVL NAME
---------- ---------- ---------------
66 1 A, E
66 2 C, D
77 1 B
77 2 C
SQL>
LISTAGG gives duplicate values if you have duplicate values
trns_id Lvl usr_id
77 2 3
77 2 3
You can remove duplicates first:
select trns_id, Lvl, LISTAGG(name)
from (
select distinct l.trns_id l.Lvl, u.name
from User_details u
join Level_details l on l.usr_id=u.ID
)
group by trns_id, Lvl
can you please explain why the error ORA-00918 is generated while executing this query
INSERT INTO CLG_TEST_2 (CLG_TEST_2.record_id, CLG_TEST_2.chain_id,
CLG_TEST_2.chain_n,
CLG_TEST_2.contact_info)
select * from (
SELECT 1, 1, 0, '2222' from dual UNION ALL
SELECT 2, 2, 0, '4444' from dual UNION ALL
SELECT 3, 3, 0, '6666' from dual
)
Error at line 1
ORA-00918: column ambiguously defined
Script Terminated on line 2.
The issue is in the fact that you are using a select * over a query without giving aliases to the columns; this will work:
INSERT INTO CLG_TEST_2 (CLG_TEST_2.record_id,
CLG_TEST_2.chain_id,
CLG_TEST_2.chain_n,
CLG_TEST_2.contact_info)
select *
from (
SELECT 1 a, 1 b, 0 c, '2222' d from dual UNION ALL
SELECT 2 , 2 , 0 , '4444' from dual UNION ALL
SELECT 3 , 3 , 0 , '6666' from dual
)
However, you can simplify your code:
INSERT INTO CLG_TEST_2 (record_id, chain_id, chain_n, contact_info)
SELECT 1, 1, 0, '2222' from dual UNION ALL
SELECT 2, 2, 0, '4444' from dual UNION ALL
SELECT 3, 3, 0, '6666' from dual
Something more about the reason of the error.
Your code:
SQL> INSERT INTO CLG_TEST_2 (
2 CLG_TEST_2.record_id,
3 CLG_TEST_2.chain_id,
4 CLG_TEST_2.chain_n,
5 CLG_TEST_2.contact_info)
6 select * from (
7 SELECT 1, 1, 0, '2222' from dual UNION ALL
8 SELECT 2, 2, 0, '4444' from dual UNION ALL
9 SELECT 3, 3, 0, '6666' from dual
10 );
select * from (
*
ERROR at line 6:
ORA-00918: column ambiguously defined
Slightly different:
SQL> INSERT INTO CLG_TEST_2 (
2 CLG_TEST_2.record_id,
3 CLG_TEST_2.chain_id,
4 CLG_TEST_2.chain_n,
5 CLG_TEST_2.contact_info)
6 select * from (
7 SELECT 1, 2, 0, '2222' from dual UNION ALL
8 SELECT 2, 2, 0, '4444' from dual UNION ALL
9 SELECT 3, 3, 0, '6666' from dual
10 );
3 rows created.
What's different?
In the first row, I changed
SELECT 1, 1, 0, '2222' --> SELECT 1, 2, 0, '2222'
^ ^
The reason:
SQL> SELECT 1, 2, 0, '2222' from dual UNION ALL
2 SELECT 2, 2, 0, '4444' from dual UNION ALL
3 SELECT 3, 3, 0, '6666' from dual;
1 2 0 '222
---------- ---------- ---------- ----
1 2 0 2222
2 2 0 4444
3 3 0 6666
SQL> SELECT 1, 1, 0, '2222' from dual UNION ALL
2 SELECT 2, 2, 0, '4444' from dual UNION ALL
3 SELECT 3, 3, 0, '6666' from dual;
1 1 0 '222
---------- ---------- ---------- ----
1 1 0 2222
2 2 0 4444
3 3 0 6666
SQL>
Here you have two columns with the same alias '1', and this is confusing for the external select *.
Also, a direct-path insert is something different
I don't see any "Direct-Path" insert. Anyway, try this one
INSERT INTO CLG_TEST_2 (CLG_TEST_2.record_id, CLG_TEST_2.chain_id,
CLG_TEST_2.chain_n,
CLG_TEST_2.contact_info)
SELECT 1, 1, 0, '2222' from dual UNION ALL
SELECT 2, 2, 0, '4444' from dual UNION ALL
SELECT 3, 3, 0, '6666' from dual
btw, why do you use string from numbers?
I have a table including CONTRACT_ID, ADDENDUM_ID and PAYMENT_MONTH (payment dates of a yearly contract).
Everytime an update occurs for any reason in the system:
ADDENDUM_ID udates as +1
PAYMENT_MONTH records are duplicated for the remaining months
In attached picture I tried to explain in detail using an example (a contract with 3 updates).
The question is how to write a query to get a summary table ignoring the duplicated but unnecessary records (grey filled ones) because of a new update on addendum column.
Please note that there are hundreds of contracts in the original table, while the example contains only one.
Thanks in advance.
If I correctly understand your desired output, maybe this can help:
with test(contract_id, addendum_id, payment_month) as
(
select 155, 1, 1 from dual union all
select 155, 1, 2 from dual union all
select 155, 1, 3 from dual union all
select 155, 1, 4 from dual union all
select 155, 1, 5 from dual union all
select 155, 1, 6 from dual union all
select 155, 1, 7 from dual union all
select 155, 1, 8 from dual union all
select 155, 1, 9 from dual union all
select 155, 1, 10 from dual union all
select 155, 1, 11 from dual union all
select 155, 1, 12 from dual union all
select 155, 2, 5 from dual union all
select 155, 2, 6 from dual union all
select 155, 2, 7 from dual union all
select 155, 2, 8 from dual union all
select 155, 2, 9 from dual union all
select 155, 2, 10 from dual union all
select 155, 2, 11 from dual union all
select 155, 2, 12 from dual union all
select 155, 3, 10 from dual union all
select 155, 3, 11 from dual union all
select 155, 3, 12 from dual
)
select contract_id, addendum_id, payment_month from (
select
max(addendum_id) over ( partition by payment_month) as max_addendum_id,
contract_id, payment_month, addendum_id
from test
)
where max_addendum_id = addendum_id
order by 1, 2, 3
I have 3 columns a,b,c in table.i need to find the duplicates for the columns a & b but with distinct value in c column.
Maybe you need something like this:
with test(a, b, c) as (
select 1, 2, 10 from dual union all
select 1, 2, 20 from dual union all
select 4, 5, 30 from dual union all
select 4, 5, 30 from dual union all
select 3, 2, 3 from dual union all
select 6, 2, 2 from dual
)
select a, b
from test
group by a,b
having count(distinct c) > 1
That is, you need to aggregate for A,B, but only keeping pairs for which there are more DISTINCT values for column C
I currently use this method to come up with a percentage:
declare #height table
(
UserId int,
tall bit
)
insert into #height
select 1, 1 union all
select 2, 1 union all
select 6, 0 union all
select 3, 0 union all
select 7, 0 union all
select 4, 1 union all
select 8, 0 union all
select 5, 0
declare #all decimal(8,5)
select
#all = count(distinct UserId)
from #height
select
count(distinct UserId) / #all Pct
from #height
where tall = 1
Result: 0.375000000
Is there a better performing way to do this? As you can see the #height
table is hit twice.
Thanks!
This allows you to hit the table only once, and gives you the same result for your given dataset.
declare #height table
(
UserId int,
tall bit
)
insert into #height
select 1, 1 union all
select 2, 1 union all
select 6, 0 union all
select 3, 0 union all
select 7, 0 union all
select 4, 1 union all
select 8, 0 union all
select 5, 0
select SUM(convert(decimal(8,5), tall)) / convert(decimal(8,5), COUNT(*)) Pct
from #height
Depending on your requirements, this might work for duplicate userids. At least it gives the same result as yours does.
select SUM(convert(decimal(8,5), tall)) / convert(decimal(8,5), COUNT(distinct userid)) Pct
from
(select distinct UserId, tall
from #height) t
Here is an alternative query that produces your expected results. I don't know how the performance of this query compares to others, but I suspect it would be easy enough for you to test this.
declare #height table
(
UserId int,
tall bit
)
insert into #height
select 1, 1 union all
select 2, 1 union all
select 4, 1 union all
select 3, 0 union all
select 5, 0 union all
select 6, 0 union all
select 7, 0 union all
select 8, 0
Select 1.0 * Count(Distinct Case When Tall = 1 Then UserId End)
/ Count(Distinct UserId)
From #height