I have one student table which is given below
ROLL | STUDENT | SUBJECT | VERSION_ID |
1 A M 1
2 B M 2
3 C M 3
4 B S 1
5 D S 2
6 E E 1
7 F G 1
If there is atleast one record for SUBJECT='M' then retrieve latest record for SUBJECT 'M' based on version id.
ELSIF If there is atleast one record for SUBJECT='S' then retrieve latest record for SUBJECT 'S' based on version id.
ELSIF If there is atleast one record for SUBJECT='E' then retrieve latest record for SUBJECT 'E' based on version id.
Else DO NOT fetch any record.
Based on above condition I am trying to write a query or view which can satisfy all above condition and and give below output. Please suggest me possible solutions to get this.
Output
ROLL | STUDENT | SUBJECT | VERSION_ID |
3 C M 3
I have tried to get desired output using below query but I encountered with error. Please help me to solve this query with better performance.
SELECT * from ( IF EXISTS(select subject from STUDENT where SUBJECT='M') then
select * from STUDENT S1
inner join
(select S2.ROLL from STUDENT S2
where
S2.ROLL=(select S3.ROLL from STUDENT S3
where S3.SUBJECT='M'
and S3.VERSION_ID=(select MAX(S4.VERSION_ID) from STUDENT S4
GROUP BY S4.SUBJECT
HAVING S4.SUBJECT='M'))) S5
ON
S1.ROLL=S5.ROLL
ELSIF EXISTS(select subject from STUDENT where SUBJECT='S') then
select * from STUDENT S1
inner join
(select S2.ROLL from STUDENT S2
where
S2.ROLL=(select S3.ROLL from STUDENT S3
where S3.SUBJECT='S'
and S3.VERSION_ID=(select MAX(S4.VERSION_ID) from STUDENT S4
GROUP BY S4.SUBJECT
HAVING S4.SUBJECT='S'))) S5
ON
S1.ROLL=S5.ROLL
ELSIF EXISTS(select subject from STUDENT where SUBJECT='E') then
select * from STUDENT S1
inner join
(select S2.ROLL from STUDENT S2
where
S2.ROLL=(select S3.ROLL from STUDENT S3
where S3.SUBJECT='E'
and S3.VERSION_ID=(select MAX(S4.VERSION_ID) from STUDENT S4
GROUP BY S4.SUBJECT
HAVING S4.SUBJECT='E'))) S5
ON
S1.ROLL=S5.ROLL
ELSE
SELECT * FROM STUDENT WHERE 1=2);
TABLE CREATION:
Create table student (ROLL number,STUDENT varchar2(20),SUBJECT varchar2(20),VERSION_ID number );
Insert Data:
insert into STUDENT values(1,'A','M',1);
insert into STUDENT values(2,'B','M',2);
insert into STUDENT values(3,'C','M',3);
insert into STUDENT values(4,'B','S',1);
insert into STUDENT values(5,'D','S',2);
insert into STUDENT values(6,'E','E',1);
insert into STUDENT values(7,'F','G',1);
Thank You..
We can apply ROW_NUMBER to entire table, using the ordering logic you gave in your question. Subjects M, S, and E have priority, in that order, with the VERSION_ID breaking the tie. Rows with a subject not matching these three are assigned the lowest priority. Should there be no records with matching subjects, we filter out all records in the WHERE clause.
WITH cte AS (
SELECT ROLL, STUDENT, SUBJECT, VERSION_ID,
ROW_NUMBER() OVER (ORDER BY CASE WHEN SUBJECT = 'M' THEN 1
WHEN SUBJECT = 'S' THEN 2
WHEN SUBJECT = 'E' THEN 3
ELSE 4 END, VERSION_ID DESC END) rn
)
SELECT ROLL, STUDENT, SUBJECT, VERSION_ID
FROM cte
WHERE rn = 1 AND SUBJECT IN ('M', 'S', 'E')
You want right answer right? Try this :
SELECT TOP 1 #student.*
FROM student
JOIN (
SELECT MAX(VERSION_ID) VERSION_ID,SUBJECT
FROM student
WHERE SUBJECT = 'M'
GROUP BY SUBJECT
UNION ALL
SELECT MAX(VERSION_ID),SUBJECT
FROM student
WHERE SUBJECT = 'S'
GROUP BY SUBJECT
UNION ALL
SELECT MAX(VERSION_ID),SUBJECT
FROM student
WHERE SUBJECT = 'E'
GROUP BY SUBJECT
UNION ALL
SELECT MAX(VERSION_ID),SUBJECT
FROM student
WHERE SUBJECT = 'G'
GROUP BY SUBJECT
UNION ALL
SELECT NULL,NULL
) AS Dtls
ON (student.SUBJECT = Dtls.SUBJECT
AND student.VERSION_ID = Dtls.VERSION_ID)
Related
I've two tables like this.
create table teams (
"ID" Integer NOT NULL ,
"STADIUM_ID" Integer NOT NULL ,
"NAME" Varchar2 (50) NOT NULL ,
primary key ("ID")
) ;
create table matches (
"ID" Integer NOT NULL ,
"WINNER_ID" Integer NOT NULL ,
"OPPONENT_ID" Integer NOT NULL ,
"WINNERSCORE" Integer,
"OPPONENTSCORE" Integer,
primary key ("ID","WINNER_ID","OPPONENT_ID")
) ;
They have the following data :
select * from matches;
ID WINNER_ID OPPONENT_ID WINNERSCORE OPPONENTSCORE
---------- ---------- ----------- ----------- -------------
1 5 2 5 2
2 4 5 1 0
3 3 2 1 0
4 3 2 1 0
5 1 2 2 0
6 3 1 2 1
select * from teams;
ID STADIUM_ID NAME
---------- ---------- -----------
1 1 Team1
2 3 Team2
3 4 Team3
4 2 Team4
5 5 Team5
I need to get the sum of the goals for each team.
For this aim, tried the following query and got the results below :
select name,
(select sum(opponentscore)
from matches
where opponent_id = teams.id) +
(select sum(winnerscore) from matches where winner_id = teams.id) sum
from teams;
NAME SUM
-------------------------------------------------- ----------
Team1 3
Team2
Team3
Team4
Team5 5
Do you have any suggestion ?
All you need is to calculate seperately opponentscore and winnerscore by each individual team, and combine them with UNION ALL :
select name, sum(score) total_score
from
(
select name, sum(winnerscore) score
from teams t join matches m on ( t.id = m.winner_id )
group by name
union all
select name, sum(opponentscore) score
from teams t join matches m on ( t.id = m.opponent_id )
group by name
)
group by name
order by 1;
SQL Fiddle Demo
you should use join and group by
select name, sum(matches.opponentscore) + sum(matches.winnerscore) my_sum
from matches
inner join teams on teams.id = matches.winner_id
group by teams.name
You could join table teams twice with table matches:
SELECT name, SUM(wonMatches.WINNERSCORE + lostMatches.OPPONENTSCORE) as goals
FROM (teams INNER JOIN matches as wonMatches ON teams.ID = wonMatches.WINNER_ID)
INNER JOIN matches as lostMatches ON teams.ID = lostMatches.OPPONENT_ID
GROUP BY name
My solution is : change your database schema. Restart thinking your app's requirements. This schema does not answer the value your user are expecting from you.
From what I see, I would say that you're trying to build an app for fans that want to track their team / favorite player progress so they can brag.
That being said, I would have, at the end, those tables :
fan
team (id_team)
player (id_player, id_team)
tournament (id_tournament)
match (id_match, id_tournament, start_on, id_team_home, id_team_visitor)
goals (id_match, id_player, goaled_on)
So now, I believe that writing your query would be much more simple. You'll just have to join team, player, count over goals and group by team.
The problem is with NULLs - the subqueries return NULL when no result is found, and NULL + anything == NULL.
Most straightforward fix is:
select name,
nvl(
(select sum(opponentscore) from matches where opponent_id = teams.id),
0
)
+
nvl(
(select sum(winnerscore) from matches where winner_id = teams.id),
0
) sum
from teams;
For performance reasons tohugh, you might want to consider using a joined query with GROUP BY as suggested by others.
I am relatively new to SQL and I may be over thinking it but given the following table.I want a list of all students that are enrolled in Chemistry but not Mathematics.So given the data I would have Student 1.
Student_ID Subject
1 Chemistry
2 Mathematics
2 Chemistry
3 History
Here's what I tried
SELECT Student_ID
FROM Student
WHERE (Subject = 'Chemistry') AND (Subject <> 'Mathematics')
Perhaps I need to group somewhere because rows and returned that exist for both criteria.
Here's one option using conditional aggregation:
select student_id
from student
group by student_id
having max(case when subject = 'Chemistry' then 1 end) = 1 and
max(case when subject = 'Mathematics' then 1 else 0 end) != 1
And here's another option using not exists:
select student_id
from student s
where subject = 'Chemistry' and not exists (
select 1
from student s2
where s.student_id = s2.student_id and st.subject = 'Mathematics'
)
You can use where and not in
select Student_ID
from Student
where Subject = 'Chemistry'
and Student_ID NOT IN ( select Student_ID from Student where subject ='Mathematics');
Say I have a table A that contains a list of a potential employees ID's and their professional skills in the form of a skill code:
ID | skill code
005 12
005 3
007 42
007 8
013 6
013 22
013 18
And I have another table B that lists several job position ID's and their corresponding required skill ID's:
Job ID | skill code
1 3
1 32
1 21
1 44
2 15
2 62
.
.
.
How can I find out which Job Id's a specific person is qualified for? I need to select all Job Id's that contain all the person's skills. Say for instance I need to find all job ID's that employee ID 003 is qualified for, how would I structure an Oracle SQL query to get this information?
I want to be able to enter any employee ID in a WHERE clause to find what jobs that person is qualified for.
An idea would be to count the number of skills for every person and job:
SELECT A.id as person_id,
B.JOB_ID
FROM A
JOIN B
ON A.skill_code=B.skill_code
GROUP BY a.id, b.job_id
HAVING count(*) = (select count(*) from b b2 where b2.job_id = b.job_id);
Not tested and assuming that tables are well normalized.
UPDATE after the OP's comment.
It is asked for all the jobs which necessitate all skills of a person:
SELECT A.id as person_id,
B.JOB_ID
FROM A
JOIN B
ON A.skill_code=B.skill_code
GROUP BY a.id, b.job_id
HAVING count(*) = (select count(*) from a a2 where a2.job_id = b.job_id);
Update2: The question was updated with:
I want to be able to enter any employee ID in a WHERE clause to find what jobs that person is qualified for.
For this, you just add WHERE a.id = :emp_id to the first query. (above group by)
Try this one
WITH b1 AS
(SELECT job_id,
skill,
COUNT(*) over (partition BY job_id order by job_id) rr
FROM b
) ,
res1 AS
(SELECT a.id,
b1.job_id,
rr,
COUNT(*) over (partition BY id, job_id order by id) rr2
FROM A
JOIN B1
ON A.skill=B1.skill
)
SELECT id, job_id FROM res1 WHERE rr=rr2
I have this table
name | prof |grade
------------------------
yossi math 100
tami math 70
yossi phisic 100
tami phisic 100
oren math 100
oren phisic 80
dor history 70
The query should return name of student which have grade 100 in math and phisic
The correct unswer is yossi
I used the following
SELECT name FROM [dbo].[Class_grade]
where prof in ('math', 'phisic') and grade = 100
but it returns more names why?
what is the right query?
Thanks
select name
from Class_Grade
where grade = 100 and
prof in ('math', 'phisic')
group by name
having count(distinct prof) = 2
Group by name and filter out the rows using having. Make sure you count the distinct occurrences of prof. In this case it is 2 because you have 2 values in your in clause.
Earlier i misquoted the Question
You can use Group by caluse
SELECT name FROM [Class_grade]
where prof in ('math', 'phisic') and grade = 100
group by (name)
having count(1)=2
SQL FIDDLE
Try this
SELECT name FROM Class_grade
WHERE prof IN ('math', 'phisic') AND grade=100
GROUP BY name
HAVING COUNT(name) > 1
group by and make suure you get two rows
SELECT name
FROM Class_grade
WHERE prof in ('math', 'phisic') and grade = 100
GROUP BY name
HAVING count(*) = 2
http://www.sqlfiddle.com/#!3/9308c/6
EDIT
you can grab distinct grades if the same student can have more than one 100% in a subject
SELECT name
FROM (
SELECT DISTINCT name, prof, grade
FROM Class_grade
WHERE prof in ('math', 'phisic') and grade = 100
) class_grade
GROUP BY name
HAVING count(*) = 2
http://www.sqlfiddle.com/#!3/79fd0/11
The OP did not state a few details:
What is the primary key on the table (i.e. can multiple lines exist for the same student for the same subject)
What is the expected result when there are multiple students who match the criteria.
If we assume that one student can have multiple grades in the same subject, many of the other answers would be wrong with the HAVING COUNT(*) clauses. Mikael Eriksson's answer satisfies that assumption, and it is possibly more performant than my below solution (though functionally different a bit too):
SELECT DISTINCT name FROM [dbo].[Class_grade] cg1
WHERE EXISTS (
SELECT 1 FROM [dbo].[Class_grade] cg2
WHERE cg2.name = cg1.name
AND cg2.prof = 'math'
AND cg2.grade = 100)
AND EXISTS (
SELECT 1 FROM [dbo].[Class_grade] cg2
WHERE cg2.name = cg1.name
AND cg2.prof = 'phisic'
AND cg2.grade = 100)
AND NOT EXISTS (
SELECT 1 FROM [dbo].[Class_grade] cg2
WHERE cg2.name = cg1.name
AND cg2.prof in ('math','phisic')
AND cg2.grade < 100)
The difference in the above code is that it will only select students who have only grade = 100 for the math and phisic subjects, even if they could have more than one grade per subject. See here.
select Class_grade.name
from Class_grade
inner join Class_grade Class_grade2
on Class_grade.Name = Class_grade2.name
where Class_grade.Prof = 'math' and Class_grade.grade = 100
and Class_grade2.prof = 'phisic' and Class_grade2.grade = 100
group by Class_grade.Name
try not to use distinct, its a poor coding
Try this:
SELECT DISTINCT name FROM [dbo].[Class_grade] where prof in ('math', 'phisic') and grade = 100
This would work as well:
SELECT DISTINCT name FROM [dbo].[Class_grade] where (prof = 'math' and grade = 100) OR (prof = 'phisics' and grade = 100)
I have a table with Student ID's and Student Names. There has been issues with assigning unique Student Id's to students and Hence I want to find the duplicates
Here is the sample Table:
Student ID Student Name
1 Jack
1 John
1 Bill
2 Amanda
2 Molly
3 Ron
4 Matt
5 James
6 Kathy
6 Will
Here I want a third column "Duplicate_Count" to display count of duplicate records.
For e.g. "Duplicate_Count" would display "3" for Student ID = 1 and so on. How can I do this?
Thanks in advance
Select StudentId, Count(*) DupCount
From Table
Group By StudentId
Having Count(*) > 1
Order By Count(*) desc,
Select
aa.StudentId, aa.StudentName, bb.DupCount
from
Table as aa
join
(
Select StudentId, Count(*) as DupCount from Table group by StudentId
) as bb
on aa.StudentId = bb.StudentId
The virtual table gives the count for each StudentId, this is joined back to the original table to add the count to each student record.
If you want to add a column to the table to hold dupcount, this query can be used in an update statement to update that column in the table
This should work:
update mytable
set duplicate_count = (select count(*) from mytable t where t.id = mytable.id)
UPDATE:
As mentioned by #HansUp, adding a new column with the duplicate count probably doesn't make sense, but that really depends on what the OP originally thought of using it for. I'm leaving the answer in case it is of help for someone else.