Subquery in sql to solve problems displaying name in exactly 2 subjects - mysql

to display the name of all the candidates who got below 40 in exactly 2 subjects using sql
degree(degcode,name,subject)
candidate(seatno,degcode,name)
marks(seatno,dedcode,mark)

Your query can be like below-
SELECT cd.name
FROM degree dg
JOIN candidate cd ON cd.degcode=dg.degcode
JOIN marks mk ON mk.seatno=cd.seatno
WHERE mk.mark < 40
GROUP BY cd.seatno
HAVING COUNT(dg.degcode)=2;
If it does not work for you then can create a sqlfiddle with dummy data for more clairty so that I can modify query as per your requirement.

Related

max and count on joing n to n tables

I'm blocked with a query and I'm needing some help.
If someone could help me I'd appreciate a lot :)
I have two tables (I'm using only one movie for showing the situation):
Table Consumption
client_id movie_id name date_consumption
XXX 1 MovieA 01/Jan/2000
YYY 1 MovieA 01/Jan/2000
ZZZ 1 MovieA 02/Jan/2000
XXX 1 MovieA 02/Jan/2000
ZZZ 1 MovieA 10/Jan/2000
Table movies_owners
movie_id rightowner date_buyed*
A LucasFilm 01/Jan/2000
A Disney 02/Jan/2000
A Sony 05/Jan/2000
**Date_buyed : It's the date where the movie belongs to a new right owner.*
The ideia is simple:
I have to find the count of clients who watched a movie for day with
the correct Right Owner in the day this movie was watched.
Table Expected
movie_id date_consumption rightowner consumption(count)
MovieA 01/Jan/2000 LucasFilm 2
MovieA 02/Jan/2000 Disney 2
MovieA 10/Jan/2000 Sony 1
--
With this query I can find the correct right owner of the movie in some day (max of all buyed dates before the day in question):
SELECT A.movie_id, A.date_buyed, A.rightowner
FROM movies_owners A
WHERE A.date_buyed EXISTS (
SELECT max(date_buyed)
FROM movies_owners
WHERE TO_DATE(date_buyed) <= TO_DATE('2000-01-02') AND movie_id = 'MovieA')
AND movie_id = 'MovieA';
But my problem is when joing with the consumption table.
I can't use the date_consumption from table consumption in a sub query.
I tried to break into a auxiliar table for doing the join, but I still can't find the result. =\
Can someone has, at least, an ideia or suggestion for me please?
Thank you all in advanced.
Juste for info: I'm working with Hive, but the sintax is almost the same from Sql.
Hive does not support non-equijoins. Move join on c.consumption_date<=o.date_buyed condition to the WHERE clause:
select c.movie_id, c.date_consumption, o.rightowner, c.consumption_count
from
(--consumption count per movie, date
select substr(movie_id,6) movie_id, date_consumption, count(*) consumption_count
from consumption
group by substr(movie_id,6), date_consumption
)c
left join movies_owners o on c.movie_id=o.movie_id
where c.consumption_date<=o.date_buyed

Complex SQL Select query with inner join

My SQL query needs to return a list of values alongside the date, but with my limited knowledge I have only been able to get this far.
This is my SQL:
select lsu_students.student_grouping,lsu_attendance.class_date,
count(lsu_attendance.attendance_status) AS count
from lsu_attendance
inner join lsu_students
ON lsu_students.student_grouping="Central1A"
and lsu_students.student_id=lsu_attendance.student_id
where lsu_attendance.attendance_status="Present"
and lsu_attendance.class_date="2015-02-09";
This returns:
student_grouping class_date count
Central1A 2015-02-09 23
I want it to return:
student_grouping class_date count
Central1A 2015-02-09 23
Central1A 2015-02-10 11
Central1A 2015-02-11 21
Central1A 2015-02-12 25
This query gets the list of the dates according to the student grouping:
select distinct(class_date)from lsu_attendance,lsu_students
where lsu_students.student_grouping like "Central1A"
and lsu_students.student_id = lsu_attendance.student_id
order by class_date
I think you just want a group by:
select s.student_grouping, a.class_date, count(a.attendance_status) AS count
from lsu_attendance a inner join
lsu_students s
ON s.student_grouping = 'Central1A' and
s.student_id = a.student_id
where a.attendance_status = 'Present'
group by s.student_grouping, a.class_date;
Comments:
Using single quotes for string constants, unless you have a good reason.
If you want a range of class dates, then use a where with appropriate filtering logic.
Notice the table aliases. The query is easier to write and to read.
I added student grouping to the group by. This would be required by any SQL engine other than MySQL.
Just take out and lsu_attendance.class_date="2015-02-09" or change it to a range, and then add (at the end) GROUP BY lsu_students.student_grouping,lsu_attendance.class_date.
The group by clause is what you're looking for, to limit aggregates (e.g. the count function) to work within each group.
To get the number of students present in each group on each date, you would do something like this:
select student_grouping, class_date, count(*) as present_count
from lsu_students join lsu_attendance using (student_id)
where attendance_status = 'Present'
group by student_grouping, class_date
Note: for your example, using is simpler than on (if your SQL supports it), and putting the table name before each field name isn't necessary if the column name doesn't appear in more than one table (though it doesn't hurt).
If you want to limit which data rows get included, put your constraints get in the where clause (this constrains which rows are counted). If you want to constrain the aggregate values that are displayed, you have to use the having clause. For example, to see the count of Central1A students present each day, but only display those dates where more than 20 students showed up:
select student_grouping, class_date, count(*) as present_count
from lsu_students join lsu_attendance using (student_id)
where attendance_status = 'Present' and student_grouping = 'Central1A'
group by student_grouping, class_date
having count(*) > 20

MySQL Return id where occurance count > attribute value

I have two table:
Bike__________________________ Kiosk
With columns:
BikeID, Location_________________ KioskID, Capacity
and data such as:
1, 1 ___________________________ 1, 10
2, 1 ___________________________ 2, 5
3, 1 ___________________________ 3, 15
4, 2
5, 1
etc... Location is a foreign key that points to kioskid. I am trying to write a query that returns only the KioskIDs of kiosks that have capacity. In other words, if 7 bikes are parked at kiosk 1, kiosk 1 is returned. If 5 bikes are parked at kiosk 2 it is not returned. I was able to write code that returns the count of bikes at each kiosk, but am confused as to how to use this (nested query?) to return only the kiosks whose capacity>count(*).
SELECT k.kioskid, COUNT(*)
FROM kiosk AS k
JOIN bike AS b ON b.location = k.kioskid
GROUP BY k.kioskid
You were almost there. All that's needed is a HAVING clause to compare the amount of bikes per kiosk to the capacity.
SQL Fiddle
SELECT k.kioskid
FROM kiosk k
left outer join bike b on b.location = k.kioskid
GROUP BY
k.kioskid
HAVING
COUNT(*) < MAX(k.Capacity)
As a sidenote, I strongly recommend to rename the location column to kioskid as to implicitly make it clear what the foreign key relation is.
I think you might be looking for HAVING as in:
SELECT k.kioskid, COUNT(kiosk.location) AS cap
FROM kiosk AS k
JOIN bike AS b ON b.location = k.kioskid
GROUP BY k.kioskid HAVING cap > k.capacity
Correct code:
SELECT kioskid, COUNT(location), capacity
FROM kiosk AS k
JOIN bike ON location = kioskid
GROUP BY kioskid
HAVING COUNT(location) < capacity;
Two issues I found with this error after delving into the issue further. 1. since MySQL works inside out, so any alias established by the AS clause must exist in the inner most code -- inn this case the HAVING clause. 2. The SQL standard requires that HAVING must reference only columns in the GROUP BY clause or columns used in aggregate functions. However, MySQL supports an extension to this behavior, and permits HAVING to refer to columns in the SELECT list and columns in outer subqueries as well. So by removing all aliases and including capacity in the SELECT clause, I got the code to work finally. Thanks #Lieven Keersmaekers and #Jim Dennis for your help.

How do I compute a ranking with MySQL stored procedures?

Let's assume we have this very simple table:
|class |student|
---------------
Math Alice
Math Bob
Math Peter
Math Anne
Music Bob
Music Chis
Music Debbie
Music Emily
Music David
Sports Alice
Sports Chris
Sports Emily
.
.
.
Now I want to find out, who I have the most classes in common with.
So basically I want a query that gets as input a list of classes (some subset of all classes)
and returns a list like:
|student |common classes|
Brad 6
Melissa 4
Chris 3
Bob 3
.
.
.
What I'm doing right now is a single query for every class. Merging the results is done on the client side. This is very slow, because I am a very hardworking student and I'm attending around 1000 classes - and so do most of the other students. I'd like to reduce the transactions and do the processing on the server side using stored procedures. I have never worked with sprocs, so I'd be glad if someone could give me some hints on how to do that.
(note: I'm using a MySQL cluster, because it's a very big school with 1 million classes and several million students)
UPDATE
Ok, it's obvious that I'm not a DB expert ;) 4 times the nearly the same answer means it's too easy.
Thank you anyway! I tested the following SQL statement and it's returning what I need, although it is very slow on the cluster (but that will be another question, I guess).
SELECT student, COUNT(class) as common_classes
FROM classes_table
WHERE class in (my_subject_list)
GROUP BY student
ORDER BY common_classes DESC
But actually I simplified my problem a bit too much, so let's make a bit it harder:
Some classes are more important than others, so they are weighted:
| class | importance |
Music 0.8
Math 0.7
Sports 0.01
English 0.5
...
Additionally, students can be more ore less important.
(In case you're wondering what this is all about... it's an analogy. And it's getting worse. So please just accept that fact. It has to do with normalizing.)
|student | importance |
Bob 3.5
Anne 4.2
Chris 0.3
...
This means a simple COUNT() won't do it anymore.
In order to find out who I have the most in common with, I want to do the following:
map<Student,float> studentRanking;
foreach (Class c in myClasses)
{
float myScoreForClassC = getMyScoreForClass(c);
List students = getStudentsAttendingClass(c);
foreach (Student s in students)
{
float studentScoreForClassC = c.classImportance*s.Importance;
studentRanking[s] += min(studentScoreForClassC, myScoreForClassC);
}
}
I hope it's not getting too confusing.
I should also mention that I myself am not in the database, so I have to tell the SELECT statement / stored procedure, which classes I'm attending.
SELECT
tbl.student,
COUNT(tbl.class) AS common_classes
FROM
tbl
WHERE tbl.class IN (SELECT
sub.class
FROM
tbl AS sub
WHERE
(sub.student = "BEN")) -- substitue "BEN" as appropriate
GROUP BY tbl.student
ORDER BY common_classes DESC;
SELECT student, COUNT(class) as common_classes
FROM classes_table
WHERE class in (my_subject_list)
GROUP BY student
ORDER BY common_classes DESC
Update re your question update.
Assuming there's a table class_importance and student_importance as you describe above:
SELECT classes.student, SUM(ci.importance*si.importance) AS weighted_importance
FROM classes
LEFT JOIN class_importance ci ON classes.class=ci.class
LEFT JOIN student_importance si ON classes.student=si.student
WHERE classes.class in (my_subject_list)
GROUP BY classes.student
ORDER BY weighted_importance DESC
The only thing this doesn't have is the LEAST(weighted_importance, myScoreForClassC) because I don't know how you calculate that.
Supposing you have another table myScores:
class | score
Math 10
Sports 0
Music 0.8
...
You can combine it all like this (see the extra LEAST inside the SUM):
SELECT classes.student, SUM(LEAST(m.score,ci.importance*si.importance)) -- min
AS weighted_importance
FROM classes
LEFT JOIN class_importance ci ON classes.class=ci.class
LEFT JOIN student_importance si ON classes.student=si.student
LEFT JOIN myScores m ON classes.class=m.class -- add in myScores
WHERE classes.class in (my_subject_list)
GROUP BY classes.student
ORDER BY weighted_importance DESC
If your myScores didn't have a score for a particular class and you wanted to assign some default, you could use IFNULL(m.score,defaultvalue).
As I understand your question, you can simply run a query like this:
SELECT `student`, COUNT(`class`) AS `commonClasses`
FROM `classes_to_students`
WHERE `class` IN ('Math', 'Music', 'Sport')
GROUP BY `student`
ORDER BY `commonClasses` DESC
Do you need to specify the classes? Or could you just specify the student? Knowing the student would let you get their classes and then get the list of other students who share those classes.
SELECT
otherStudents.Student,
COUNT(*) AS sharedClasses
FROM
class_student_map AS myClasses
INNER JOIN
class_student_map AS otherStudents
ON otherStudents.class = myClasses.class
AND otherStudents.student != myClasses.student
WHERE
myClasses.student = 'Ben'
GROUP BY
otherStudents.Student
EDIT
To follow up your edit, you just need to join on the new table and do your calculation.
Using the SQL example you gave in the edit...
SELECT
classes_table.student,
MIN(class_importance.importance * student_importance.importance) as rank
FROM
classes_table
INNER JOIN
class_important
ON classes_table.class = class_importance.class
INNER JOIN
student_important
ON classes_table.student = student_importance.student
WHERE
classes_table.class in (my_subject_list)
GROUP BY
classes_table.student
ORDER BY
2

find out count of comma based value in MySql

I have two tables.
Table Emp
id name
1 Ajay
2 Amol
3 Sanjay
4 Vijay
Table Sports
Sport_name Played by
Cricket ^2^,^3^,^4^
Football ^1^,^3^
Vollyball ^4^,^1^
Now I want to write a query which will give me output like
name No_of_sports_played
Ajay 2
Amol 1
Sanjay 2
Vijay 2
So what will be Mysql query for this?
I agree with the above answers/comments that you are not using a database for what a database is for, but here is how you could calculate your table from your current structure in case you have no control over that:
SELECT Emp.name, IF(Played_by IS NULL,0,COUNT(*)) as Num_Sports
FROM Emp
LEFT JOIN Sports
ON Sports.Played_by RLIKE CONCAT('[[:<:]]',Emp.id,'[[:>:]]')
GROUP BY Emp.name;
See it in action here.
UPDATE: added the IF(Played_by IS NULL,0,COUNT(*)) instead of COUNT(*). This means that if an employee doesn't play anything they'll have a 0 as their Num_Sports. See it here (I also added in those ^ characters and it still works.
What it does is joins the Emp table to the Sports table if it can find the Emp.id in the corresponding Played_by column.
For example, if we wanted to see what sports Ajay played (id=1), we could do:
SELECT *
FROM Emp, Sports
WHERE Sports.Played_by LIKE '%1%'
AND Emp.id=1;
The query I gave as my solution is basically the query above, with a GROUP BY Emp.name to perform it for each employee.
The one modification is the use of RLIKE instead of LIKE.
I use RLIKE '[[:<:]]employeeid[[:>:]]' instead of LIKE '%employeeid%. The [[:<:]] symbols just mean "make sure the employeeid you match is a whole word".
This prevents (e.g.) Emp.id 1 matching the 1 in the Played_by of 3,4,11,2.
You do not want to store your relationships in a column like that. Create this table:
CREATE TABLE player_sports (player_id INTEGER NOT NULL, sport_id INTEGER NOT NULL, PRIMARY KEY(player_id, sport_id));
This assumes you have an id column in your sports table. So now a player will have one record in player_sports for each sport they play.
Your final query will be:
SELECT p.name, COUNT(ps.player_id)
FROM players p, player_sports ps
WHERE ps.player_id = p.id
GROUP BY p.name;