how to get Intersection of two query in mysql - mysql

I have a table that has two column with two foreign key from two different table.
this is my relation table:
I want to select those student who can speak both language with id 3 and 4.
How can i wrote a query to give me for e.x 12 , 14

You can give it a try:
SELECT
student_id,
COUNT(*) total
FROM your_table
WHERE language_id IN (3,4)
GROUP BY student_id
HAVING COUNT(*) = 2;
Only IN doesn't ensure that a student is involved both in language id 3 & 4.
You need to use GROUP BY student_id HAVING COUNT(*) = 2 in order to ensure those student_ids to be in the result who were involved both in language id 3 & 4
Another solution would be using INNER JOIN. But it doesn't scale.
SELECT
A.student_id
FROM your_table A
INNER JOIN your_table B ON A.student_id = B.student_id
AND A.language_id = 3 AND B.language_id = 4

Assume your relation is named "my-relation":
SELECT R1.student_Id FROM my-Relation R1 join my-Relation R2 on R1.student_id = R2.student_id where R1.language_Id = '3' and R2.language_id = '4'

Related

Aggregating three tables but getting wrong values during the aggregation operation

"employee" Table
emp_id
empName
1
ABC
2
xyx
"client" Table:
id
emp_id
clientName
1
1
a
2
1
b
3
1
c
4
2
d
"collection" Table
id
emp_id
Amount
1
2
1000
2
1
2000
3
1
1000
4
1
1200
I want to aggregate values from the three tables input tables here reported as samples. For each employee I need to find
the total collection amount for that employee (as a sum)
the clients that are involved with the corresponding employee (as a comma-separated value)
Here follows my current query.
MyQuery:
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
SUM(collection.Amount)
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN collection
ON collection.emp_id = employee.emp_id
GROUP BY employee.emp_id;
The problem of this query is that I'm getting wrong values of sums and clients when an employee is associated to multiple of them.
Current Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a,b,c,c,b,a,a,b,c
8400
2
xyz
d,d
1000
Expected Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a , b , c
4200
2
xyz
d
1000
How can I solve this problem?
There are some typos in your query:
the separator inside the GROUP_CONCAT function should be a comma instead of a space, given your current output, though comma is default value, so you can really omit that clause.
each alias in your select requires the table where it comes from, as long as those field names are used in more than one tables among the ones you're joining on
your GROUP BY clause should at least contain every field that is not aggregated inside the SELECT clause in order to have a potentially correct output.
The overall conceptual problem in your query is that the join combines every row of the "employee" table with every row of the "client" table (resulting in multiple rows and higher sum of amounts during the aggregation). One way for getting out of the rabbit hole is a first aggregation on the "client" table (to have one row for each "emp_id" value), then join back with the other tables.
SELECT emp.emp_id,
emp.empName,
cl.clientName,
SUM(coll.Amount)
FROM employee emp
LEFT JOIN (SELECT emp_id,
GROUP_CONCAT(client.clientName
ORDER BY client.id) AS clientName
FROM client
GROUP BY emp_id) cl
ON cl.emp_id = emp.emp_id
LEFT JOIN (SELECT emp_id, Amount FROM collection) coll
ON coll.emp_id = emp.emp_id
GROUP BY emp.emp_id,
emp.empName,
cl.clientName
Check the demo here.
Regardless of my comment, here is a query for your desired output:
SELECT
a.emp_id,
a.empName,
a.clientName,
SUM(col.Amount) AS totalCollection
FROM (SELECT e.emp_id,
e.`empName`,
GROUP_CONCAT(DISTINCT c.clientName ORDER BY c.id ) AS clientName
FROM employee e
LEFT JOIN `client` c
ON c.emp_id = e.emp_id
GROUP BY e.`emp_id`) a
LEFT JOIN collection col
ON col.emp_id = a.emp_id
GROUP BY col.emp_id;
When having multiple joins, you should be careful about the relations and the number of results(rows) that your query generates. You might as well have multiple records in output than your desired ones.
Hope this helps
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
C .Amount
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN (select collection.emp_id , sum(collection.Amount ) as Amount from collection group by collection.emp_id) C
ON C.emp_id = employee.emp_id
GROUP BY employee.emp_id;
it works for me now

mysql select of select

Table Student
StudentID
StudentName
1
A
2
B
3
C
Table Book
BookID
BookName
1
Book1
2
Book2
3
Book3
Table BookAssignment
AssignID
BookID
StudentID
DateTime
1
1
1
2021-06-26
2
2
1
2021-07-01
3
1
2
2021-07-03
The result table should be
StudentID
StudentName
BookCount
1
A
2
2
B
1
3
C
0
How to get the result table in one SQL execution?
Left JOIN seems not an option since it eliminates StudentID 3
Just added another DateTime column to the BookAssignment table - What is SQL syntax to query the book count over the last 7 consecutive days (even for 0 book for day count)?
you need to use simple group by using left join between two tables:
select s.StudentID, s.StudentName , count(*) BookCount
from students s
left join books b
on s.StudentID = b.StudentID
group by s.StudentID, s.StudentName
I'd left join the student table on an aggregate query of the books and use coalesce to fill in the zeros:
SELECT s.StudentID, StudentName, COALESCE(cnt, 0)
FROM student s
LEFT JOIN (SELECT StudentID, COUNT(*) AS cnt
FROM books
GROUP BY StudentID) b ON s.StudentID = b.StudentID
You can also use a correlated subquery:
select s.*,
(select count(*)
from books b
where s.StudentID = b.StudentID
) as bookCount
from students s;
This has some advantages over using a join/group by approach:
You can trivially include all columns in the select. They don't have to repeated in the group by.
With an index on books(StudentID) this often has the best performance.
This avoids the outer aggregation, which can kill performance.
Adding another dimension (say the number of courses the student has) just works, without worrying about Cartesian Products.
select s.StudentID, s.StudentName ,(select count(*) from BookAssignment b where b.studentid = s.studentid) as BookCount
from students s

MySQL join to get a single row using order/priority

I have a couple tables in MySQL DB
EID Name
1 Title A
2 Title B
3 Title C
LID EID Location Address Order
1 1 Office NY 1
2 1 Home IL 2
3 2 Office CA 1
4 3 Home NJ 2
I have the above 2 tables (Employee and Location). I would like to know the location of each Employee with office as a preferred choice and if 'office' does not exist then would need 'Home' location . The order column defined the order/priority of what is needed.
here is the output needed
EID LID Name Location Address
1 1 Title A Office NY
2 3 Title B Office CA
3 4 Title C Home NJ
The first join of the query below just connects the Employee and Location tables, but note that it results in all records from Location being joined. The critical part of the below query is the second INNER JOIN to a subquery. This subquery identifies the minimum (i.e. highest priority) order for each employee ID. This is then used to discard records from the first join which are not the highest priority.
SELECT t1.EID,
t2.LID,
t1.Name,
t2.Location,
t2.Address
FROM Employee t1
INNER JOIN Location t2
ON t1.EID = t2.EID
INNER JOIN
(
SELECT EID, MIN(`Order`) AS min_order
FROM Location
GROUP BY EID
) t3
ON t2.EID = t3.EID AND
t2.Order = t3.min_order
One other note: Don't name your columns Order, which is a MySQL keyword. To get my query to work, I had to put it in backticks, which is inconvenient to say the least, and possibly error prone.
Demo here:
SQLFiddle
There are two posibility to get your result.
1)If you need Based on Order result then use this query
SELECT e1.EID, l1.LID, e1.Name, l1.Location, l1.Address
FROM Employee e1
JOIN
(SELECT MIN(`Order `) as Minorder, EID, LID, Location, Address, Order
FROM Location l1
GROUP BY EID) l1
ON l1.EID = e1.EID AND l1.Minorder = l1.Order;
2)if you need result Based on EID then use this query
SELECT e1.EID,l1.LID,e1.Name,l1.Location,l1.Address
FROM Employee e1 JOIN
(SELECT MIN(`EID`)as Mineid,EID,LID,Location,Address,`Order` FROM Location l1 GROUP BY EID)l1
ON l1.Mineid = e1.EID;
Extra Note:-
Plese donot use mysql inbuilt keyword as Column name or Table name for more information read this link click here
You can the expected result by using inner join
Select a.eid,b.Lid,a.name,b.location,b.address from Table1 a innner join (select * from Tableb group by eid) b on
a.eid=b.eid;
you can try this code this will help you as i think
select E.EID,E.name,ad.LID,ad.LOCATION,ad.ADDRESS,ad.[order]
from #emp E inner join #address ad on E.EID = ad.EID
inner join (select EID, min([order]) [order]
from #address
group by EID) tt on ad.EID = tt.EIDand ad.[order] = tt.[order]

MYSQL Order by sum of different table

I have two tables:
cat_seriale - which represents the serial categories and provides ID's for each category like: 1, 2, 3, 4, 5.
seriale - which is actual tv serials, and each tv serial falls in one category.
I am trying to
SELECT * FROM cat_seriale WHERE `id`='1'
and additionally to all columns, display the sum of views column from all rows in seriale table.
If some one can help me out, that would be great.
Thanks in advance.
SCHEMA:
cat_seriale columns:
Primary Key - catid(int)
catname (varchar)
...
seriale columns:
Primary Key - id(int)
cat (int)
views(int)
I need to select cat_seriale where ID = 1, and select sum of views in seriale columns where cat is same with id from cat_seriale.
Like this:
select *, (select sum(views) from seriale S where S.cat=C.catid) as sum_views
from cat_seriale C
where id='1'
order by sum_views
Join the two tables.
SELECT c.*, SUM(s.views) AS views
FROM cat_seriale AS c
LEFT JOIN seriale AS s ON c.catid = s.cat
WHERE c.id = `1`
GROUP BY c.catid
+1 for #Mike answer, but this version has more performance:
SELECT c.*, SUM(s.views) as summary
FROM cat_seriale c
LEFT JOIN seriale s ON s.cat = c.catid
WHERE c.id = '1'
GROUP BY c.catid
ORDER BY summary

SQL subquery to return MIN of a column and corresponding values from another column

I'm trying to query
number of courses passed,
the earliest course passed
time taken to pass first course, for each student who is not currently expelled.
The tricky part here is 2). I constructed a sub-query by mapping the course table onto itself but restricting matches only to datepassed=min(datepassed). The query appears to work for a very sample, but when I try to apply it to my full data set (which would return ~1 million records) the query takes impossibly long to execute (left it for >2 hours and still wouldn't complete).
Is there a more efficient way to do this? Appreciate all your help!
Query:
SELECT
S.id,
COUNT(C.course) as course_count,
C2.course as first_course,
DATEDIFF(MIN(C.datepassed),S.dateenrolled) as days_to_first
FROM student S
LEFT JOIN course C
ON C.studentid = S.id
LEFT JOIN (SELECT * FROM course GROUP BY studentid HAVING datepassed IN (MIN(datepassed))) C2
ON C2.studentid = C.studentid
WHERE YEAR(S.dateenrolled)=2013
AND U.id NOT IN (SELECT id FROM expelled)
GROUP BY S.id
ORDER BY S.id
Student table
id status dateenrolled
1 graduated 1/1/2013
3 graduated 1/1/2013
Expelled table
id dateexpelled
2 5/1/2013
Course table
studentid course datepassed
1 courseA 5/1/2014
1 courseB 1/1/2014
1 courseC 2/1/2014
1 courseD 3/1/2014
3 courseA 1/1/2014
3 couseB 2/1/2014
3 courseC 3/1/2014
3 courseD 4/1/2014
3 courseE 5/1/2014
SELECT id, course_count, days_to_first, C2.course first_course
FROM (
SELECT S.id, COUNT(C.course) course_count,
DATEDIFF(MIN(datepassed),S.dateenrolled) as days_to_first,
MIN(datepassed) min_datepassed
FROM student S
LEFT JOIN course C ON C.studentid = S.id
WHERE S.dateenrolled BETWEEN '2013-01-01' AND '2013-12-31'
AND S.id NOT IN (SELECT id FROM expelled)
GROUP BY S.id
) t1 LEFT JOIN course C2
ON C2.studentid = t1.id
AND C2.datepassed = t1.min_datepassed
ORDER BY id
I would try something like:
SELECT s.id, f.course,
COALESCE( DATEDIFF( c.first_pass,s.dateenrolled), 0 ) AS days_to_pass,
COALESCE( c.num_courses, 0 ) AS courses
FROM student s
LEFT JOIN
( SELECT studentid, MIN(datepassed) AS first_pass, COUNT(*) AS num_courses
FROM course
GROUP BY studentid ) c
ON s.id = c.studentid
JOIN course f
ON c.studentid = f.studentid AND c.first_pass = f.datepassed
LEFT JOIN expelled e
ON s.id = e.id
WHERE s.dateenrolled BETWEEN '2013-01-01' AND '2013-12-31'
AND e.id IS NULL
This query assumes a student can pass only one course on a given day, otherwise you can get more than one row for a student as its possible to have many first courses.
For performance it would help to have an index on dateenrolled in student table and a composite index on (studentid,datepassed) in courses table.