I am trying to find the second max based on two different categories. I can use analtycal function or logic to get this. I have been trying to find this through a logic.
My question is I am trying to fetch the records of second most taken exam per country by unique students.
T1
Exam_ID Student_ID
123 553
123 457
345 563
567 765
678 543
678 543
987 123
678 123
T2
Exam_ID Exam_name Country_name
123 SAT USA
345 CAT USA
567 GRE USA
678 TOEFL UK
987 IELTS UK
222 CBAP UK
This is what I tried so far,
select count(distinct T1.Student_ID) count_user,
t2.Country_name,t2.Exam_name
from T1
join T2
on T1.Exam_ID = T2.Exam_ID
group by t2.Exam_name, t2.Country_name
By doing this I am able to get the unique student count based on each exam and country.
How can I get the second max no of exams taken by unique students based on the country?
I'm not sure I fully understand what you mean by your question.
Could you post the expected result along with what you are getting now?
In the mean time, I'm taking a guess that exam_id 678 in the UK (with 3 students) is the top result and 987 in the UK is the "second top result"???
If so, Row_number () might work for you. Bear in mind that row_number is usually an expensive operation in relational databases as it involves a redistribution and a sort. A similar function Rank () may be better for you depending upon how you want to handle ties. The syntax is similar, you could try both.
Try modifying your query as follows:
select count(distinct T1.student_id) count_user, Country_name, Exam_name,
row_number () over (partition by country_name order by count_user desc) as row_num
...
If that gives you the numbering you want, you can then restrict the output using the qualify clause i.e.
qualify row_num = 2
You may need to wrap the whole thing in a derived table as follows:
select count_user, country_name, exam_name,
row_number () over (partition by country_name order by count_user desc) as row_num
from (
select count(distinct T1.Student_ID) count_user,
t2.Country_name,t2.Exam_name,
from T1 join T2
on T1.Exam_ID = T2.Exam_ID
group by t2.Exam_name, t2.Country_name
) detail_recs
qualify row_num = 2
Related
One of the test questions came by with following schemas, to look for the best doctor in terms of:
Best scored;
The most times/attempts;
For each medical procedures (in terms of name)
[doctor] table
id
first_name
last_name
age
1
Phillip
Singleton
50
2
Heidi
Elliott
34
3
Beulah
Townsend
35
4
Gary
Pena
36
5
Doug
Lowe
45
[medical_procedure] table
id
doctor_id
name
score
1
3
colonoscopy
44
2
1
colonoscopy
37
3
4
ulcer surgery
98
4
2
angiography
79
5
3
angiography
84
6
3
embolization
87
and list goes on...
Given solution as follow:
WITH cte AS(
SELECT
name,
first_name,
last_name,
COUNT(*) AS procedure_count,
RANK() OVER(
PARTITION BY name
ORDER BY COUNT(*) DESC) AS place
FROM
medical_procedure p JOIN doctor d
ON p.doctor_id = d.id
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
GROUP BY
name,
first_name,
last_name
)
SELECT
name,
first_name,
last_name
FROM cte
WHERE place = 1;
It'll mean a lot to be clarified on/explain on how the WHERE clause worked out under the subquery:
How it worked out in general
Why must we match the two pp.name and p.name for it to reflect the correct rows...
...
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
...
Thanks a heap!
Above is join with doctor and medical procedure and group by procedure name and you need doctor names with most attempt and best scored.
Subquery will join by procedure avg score and those who have better score than avg will be filtered.
Now there can be multiple doctor better than avg so taken rank by procedure count so most attempted will come first and then you taken first to pick top one
I have 2 SQL tables.
The first table has a mapping of department-ids to student-ids. (A student may belong to more than one department and a department has many students).
The second table has a mapping of students and their hobbies. (which also has a many to many relationship)
Sample Snapshots of tables:
DESC DEPARTMENT_STUDENTS;
----------------------------
DEPT_ID STUDENT_ID
----------------------------
Physics 123
Mathematics 111
Physics 111
CS 45
CS 56
Mathematics 89
DESC STUDENTS_HOBBIES;
-------------------------------
STUDENT_ID HOBBY
-------------------------------
111 Skiing
111 Singing
111 Browsing
123 Singing
123 Browsing
123 Reading
45 Origami
56 Origami
56 Making Prank Calls
89 Reading
I am looking for a query that will provide a list of common hobbies among all the students per department.
Required output would look like :
-----------------------------------------
DEPT_ID GROUP_CONCAT(...)
-----------------------------------------
Physics (Singing, Browsing)
Mathematics ()
CS (Origami)
I am almost there and after a little bit of fiddling around with MySQL, I was able to group ALL(not the common ones) the hobbies of a department. GROUP_CONCAT provides ways to eliminate duplicates also. One way that I could think of , is to retrieve duplicates programatically over the query below:
SELECT DEPT_ID, GROUP_CONCAT(OP.HOBBIES) FROM DEPARTMENT_STUDENTS DS
INNER JOIN (SELECT STUDENT_ID, GROUP_CONCAT(HOBBY)AS HOBBIES FROM STUDENTS_HOBBIES
GROUP BY STUDENT_ID) OP
ON OP.STUDENT_ID = DS.STUDENT_ID
GROUP BY DS.DEPT_ID;
Is there a way to preserve duplicates alone? Even in this query, any optimizations are welcome. Thanks!
Here you go:
SELECT DEPT_ID, GROUP_CONCAT(HOBBY) FROM (
SELECT
d.DEPT_ID
, s.HOBBY
FROM
department_students d
INNER JOIN students_hobbies s USING(STUDENT_ID)
GROUP BY d.DEPT_ID, s.HOBBY
HAVING COUNT(DISTINCT s.STUDENT_ID) > 1
)sq
GROUP BY DEPT_ID
For each identifier, how can I return the quantity when the received country is not equal to any of the delivered countries? I need an efficient query for the steps below since my table is huge.
These are the steps I would think could do this, of course you don't need to follow them :)
Create a group of 'delivered' countries for each identifier.
See if 'received' is any of these countries for each identifier. If
there is no match, return this result.
Starting Table:
identifier delivered received quantity
------------- ------------ ----------- ------------
1 USA France 432
1 France USA 450
1 Ireland Russia 100
2 Germany Germany 1,034
3 USA France 50
3 USA USA 120
Result:
identifier delivered received quantity
------------- ------------ ----------- ------------
1 Ireland Russia 100
The starting table is about 30,000,000 rows, so self-joins will be impossible unfortunately. I am using something similar to MySQL.
I think LEFT JOIN query should work for you:
SELECT a.*
FROM starting a
LEFT JOIN starting b
ON a.id = b.id
AND a.delivered = b.received
WHERE b.received IS NULL;
Example: SQLFiddle
For optimizing above query, adding following composite index should give you better performance:
ALTER TABLE starting ADD KEY ix1(id, delivered, received);
You could use a not exists subquery:
SELECT a.*
FROM starting a
WHERE NOT EXISTS
(
SELECT *
FROM starting b
WHERE a.id = b.id
AND a.delivered = b.received
)
This is not a self-join, but the query optimizer is free to execute it as one (and usually does.)
i have three tables in mysql like this,
triz_sti
stu_id name
-----------------
1 x1
2 x2
triz_sub
sub_id sub_name
------------------
1 english
2 maths
3 science
triz
stu_id sub_id marks
-------------------------
1 1 23
1 2 56
1 3 83
2 1 78
2 2 23
2 3 50
i want the result like
display all subject with higest mark in perticular subject with student name,
max_marks sub_name student_name
--------------------------------------
78 english x2
56 maths x1
83 science x2
so please help for this output that i want, i have tried but i m not get it desire output.
How about something like this?
SELECT
t.stu_id, t.sub_id, t.marks
FROM
triz t
JOIN (SELECT sub_id, MAX(marks) max_mark FROM triz GROUP BY sub_id) a ON (a.sub_id = t.sub_id AND a.max_mark = t.marks)
Of course you'll need to join it with lookup tables for names.
Have to say, it's early here so I might have missed something.
BR
The general, simplified syntax in this case is
SELECT stuff FROM joined tables ORDER BY whatever
The easiest is the ORDER BY: you want to sort descending by marks, so you ORDER BY marks DESC.
Where do the data come from? From triz, joined to the others. So
triz JOIN triz_sti USING (stu_id) JOIN triz_sub USING (sub_id)
And you want to display the marks.
So you get
SELECT marks, sub_name, name AS student_name
FROM triz JOIN triz_sti USING (stu_id) JOIN triz_sub USING (sub_id)
ORDER BY marks DESC
.
The rest I leave to you. :-)
I am writing a query against an advanced many-to-many table in my database. I call it an advanced table because it is a many-to-many table with and extra field. The table maps data between the fields table and the students table. The fields table holds potential fields that a student can used, kind of like a contact system (i.e. name, school, address, etc). The studentvalues table that I need to query against holds the field id, student id, and the field answer (i.e. studentid=1; fieldid=2; response=Dave Long).
So my table looks like this:
What I need to do is take a few passed in values and create a grouped accumulated report. I would like to do as much in the SQL as possible.
So that data that I have will be the group by field (a field id), the cumulative field (a field id) and I need to group the students by the group by field and then in each group count the amount of students in the cumulative fields.
So for example I have this data
ID STUDENTID FIELDID RESPONSE
1 1 2 *(city)* Wallingford
2 1 3 *(state)* CT
3 2 2 *(city)* Wallingford
4 2 3 *(state)* CT
5 3 2 *(city)* Berlin
6 3 3 *(state)* CT
7 4 2 *(city)* Costa Mesa
8 4 3 *(state)* CA
I am hoping to write one query that I can generate a report that looks like this:
CA - 1 Student
Costa Mesa 1
CT - 3 Students
Berlin 1
Wallingford 2
Is this possible to do with a single SQL statement or do I have to get all the groups and then loop over them?
EDIT Here is the code that I have gotten so far, but it doesn't give the proper stateSubtotal (the stateSubtotal is the same as the citySubtotal)
SELECT state, count(state) AS stateSubtotal, city, count(city) AS citySubtotal
FROM(
SELECT s1.response AS city, s2.response AS state
FROM studentvalues s1
INNER JOIN studentvalues s2
ON s1.studentid = s2.studentid
WHERE s1.fieldid = 5
AND s2.fieldid = 6
) t
GROUP BY city, state
So to make a table that looks like that, I would assume something like
State StateSubtotal City CitySubtotal
CA 1 Costa Mesa 1
CT 3 Berlin 1
CT 3 Wallingford 2
Would be what you want. We can't just group on Response, since if you had a student answer LA for city, and another student that responds LA for state (Louisiana) they would add. Also, if the same city is in different states, we need to first lay out the association between a city and a state by joining on the student id.
edit - indeed, flawed first approach. The different aggregates need different groupings, so really, one select per aggregation is required. This gives the right result but it's ugly and I bet it could be improved on. If you were on SQL Server I would think a CTE would help but that's not an option.
select t2.stateAbb, stateSubtotal, t2.city, t2.citySubtotal from
(
select city, count(city) as citySubTotal, stateAbb from (
select s1.Response as city, s2.Response as StateAbb
from aaa s1 inner join aaa s2 on s1.studentId = s2.studentId
where s1.fieldId = 2 and s2.fieldId=3
) t1
group by city, stateabb
) t2 inner join (
select stateAbb, count(stateabb) as stateSubTotal from (
select s1.Response as city, s2.Response as StateAbb
from aaa s1 inner join aaa s2 on s1.studentId = s2.studentId
where s1.fieldId = 2 and s2.fieldId=3
) t3
group by stateabb
) t4 on t2.stateabb = t4.stateabb