find missing values in MySQL - sql

I am trying to write a query to find out what users are not enrolled on some courses.
I have 2 tables; courses, and users. Both contain the fields 'id' and then 'coursename' and username' respectively.
Using these two table the existing system adds users to a third table (called enrolled_users). Users take up one row for each. For example if user id '1' is on 4 courses the enrolled_users looks like this:
-----------------------
| user_id | course_id |
-----------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
-----------------------
As you can see this table data is not built on a relationship, it is generated in PHP using the two tables 'courses' and 'users'.
I am not sure how to write a query on how to find out that courses user '1' is not enrolled in, if there are say 10 courses, but we can see user '1' is only on courses 1-4.
Am I suppose to use something like a LIKE or NOT LIKE?
SELECT u.id, c.id, ec.user_id, ec.course_id
FROM users u, courses c, enrolled_courses ec
WHERE u.id = ec.user_id
AND c.id = ec.course_id
AND u.id = '1'
I have tried using != and <> but this doesn't show what i need; a list of courses the user is not enrolled on.
Sorry if I am being vague, trying to get my head round it!
Using MySQL 5.0, on an existing system I cannot modify, only query from (for the moment).

SELECT
*
FROM
courses c
WHERE
c.id NOT IN (
SELECT
ec.course_id
FROM
enrolled_courses ec
WHERE
ec.user_id = 1 AND ec.course_id IS NOT NULL
)

If you are looking for a specific single user ID, be sure to include that ID condition as part of the WHERE clause, otherwise, leave it blank, and it will give you ALL people who are not registered for ALL possible classes via a Cartesian product (since no direct join between user and courses table) .. this could be VERY large / time consuming if trying to run for everyone and your course table is 100s of courses where someone would only be involved in 2-3-4 courses.
select
u.id,
c.id CourseID
from
users u,
courses c
where
u.id = 1
AND c.id NOT IN
( select ec.Course_ID
from enrolled_courses ec
where ec.user_id = u.id )

Just use a not in and a sub select.
select * from courses c where c.id not in
(select course_id from enrolled_courses where user_id='1')

Related

Given two related tables, how to determine the most common relationships?

given a 3 tables: users, books, book_users, how would I determine what are the commons books?
users: id, first_name, last_name
books: id, name
books_users: book_id, user_id
Designer Output, something like:
book | count
radBookName | 22
SemiRad | 22
Thanks
You seems want simple JOIN with GROUP BY clause :
SELECT b.name, count(*) as user_count
FROM books b INNER JOIN
books_users bu
ON bu.book_id = b.id
GROUP BY b.name;
This would produce duplicate count if one book has same user, if you want unique count then use count(distinct bu.user_id) instead.

Select with count on 3 tables

I need your help for a particular SELECT on 3 tables. I'm not skilled on SQL so it's a difficult SELECT for me, since I have to apply COUNT (I suppose) to the query.
I show you my tables:
I need to know how many contacts there are in the database (all the contacts!!!!) and how many photos and videos are bound to any contact.
I should get a result similar to this:
-----------------------------------
| ID | NAME | PHOTO | VIDEO |
-----------------------------------
| 1 | MARK | 3 | 1 |
-----------------------------------
| ID | LISA | 2 | 0 |
-----------------------------------
Thank you for your help
You can use the following approach, if you are hesitant about duplicates in the query you can use a sql function and pass type parameter as a string. If you have uncertain number of types (VIDEO, PHOTO, TEXT etc) you need to redesign the output table format (I would go with the following tuple TYPE, CONTACT_ID, COUNT), or at the worst case go with dynamic query construction.
select c.ID, c.NAME,
(select count(*) from CONTACT_MEDIA cm join MEDIA m on
m.ID = cm.ID_MEDIA and m.TYPE = 'PHOTO' where cm.ID_CONTACT = c.ID) as PHOTO,
(select count(*) from CONTACT_MEDIA cm join MEDIA m on
m.ID = cm.ID_MEDIA and m.TYPE = 'VIDEO' where cm.ID_CONTACT = c.ID) as VIDEO
from CONTACT c
Please use below query , this will give you exact result
select contact_media.ID_Contact, contact.Name, count(M1.ID) as 'PHOTO', COUNT(M2.ID) as 'VIDEO' from Contact inner join contact_media on Contact.ID=contact_media.ID_Contact
left outer join media M1 on contact_media.ID_Media=M1.ID and M1.TYPE='PHOTO'
left outer join media M2 on contact_media.ID_Media=M2.ID and M2.TYPE='VIDEO'
group by contact_media.ID_Contact, contact.Name

SQL query (Join without duplicates)

I have tables users and topics. Every user can have from 0 to several topics (one-to-many relationship).
How I can get only those users which have at least one topic?
I need all columns from users (without columns from topics) and without duplicates in table users. In last column I need number of topics.
UPDATED:
Should be like this:
SELECT user.*, count(topic.id)
FROM ad
LEFT JOIN topic ON user.id = topic.ad
GROUP BY user.id
HAVING count(topic.id) > 0;
but it takes 0 result. But it should not be 0.
Firstly you need to have your two tables, because you have left limited information about your table structure I will use an example to explain how this works, you should then be able to easily apply this to your own tables.
Firstly you need to have two tables (which you do)
Table "user"
id | name
1 | Joe Bloggs
2 | Eddy Ready
Table "topic"
topicid | userid | topic
1 | 1 | Breakfast
2 | 1 | Lunch
3 | 1 | Dinner
Now asking for a count against each user is done using the follwing;
SELECT user.name, count(topic.topicid)
FROM user
INNER JOIN topic ON user.id = topic.userid
GROUP BY user.name
If you use a left join, this will include records from the "user" table which does not have any rows in the "topic" table, however if you use an INNER JOIN this will ONLY include users who have a matching value in both tables.
I.e. because the user id "2" (which we use to join) is not listed in the topic table you will not get any results for this user.
Hope that helps!
use inner join and distinct
select distinct user_table.id
from user_table
inner join topics_table on topic_table.user_id = user_table.id
select u.id
, u.name
, count(b.topicName)
from user u
left join topic t on t.userid = u.id
group by u.id, u.name
You can select topic number per user and then join it with user data. Something like this:
with t as
(
select userid, count(*) as n
from topic
group by userid
)
SELECT user.*, t.n
FROM user
JOIN t ON user.id = t.userid

Tricky statistics select

Trying to figure out this for a while, but no luck. I have the following tables (MS-SQL 2008):
students
studentID – email – profileID
courses
courseID – name
studentsCourses
studentID – courseID
profiles
profileID – name
profilesMandatoryCourses
profileID - courseID
studentsCoursesLogs
logID - studentID –courseID – accessDate
Each student, when enrolls, is assigned a profile. For each profile there are a number of mandatory course. Those mandatory courses together with any other courses a user takes are saved in the studentsCourses table.
Whenever a student accesses a course the information is logged in the studentsCoursesLogs.
I am trying to figure out all the students that have taken all the mandatory courses based on their profile.
Any pointers appreciated.
I think this should do it (assuming I've understood your data model and requirements correctly - and that in throwing this sample SQL together I've not made any mistakes - I didn't create your data model in a database). I'm pretty sure it should be close enough though.
select studentRecords.StudentId,
sum(case TakenCourseID when null then 0 else 1 end) as CompleteMandatoryCourses,
sum(case TakenCourseID when null then 1 else 0 end) as IncompleteMandatoryCourses
from (
select mandatoryCourses.StudentID, mandatoryCourses.CourseId as MandatoryCourseID, takenCourses.CourseID as TakenCourseID
from ( -- Courses student should have taken - based on their profile
select p.[Profile], pmc.CourseID, s.StudentID
from profiles p
inner join profilesMandatoryCourses pmc on p.ProfileID = pmc.Profile
inner join students s on p.StudentID = s.StudentID
) mandatoryCourses
left join
(
-- Course students have taken
select s.StudentID, s.ProfileID, sc.CourseID
from students s
inner join studentsCourseLogs scl on s.StudentID = scl.StudentID
) takenCourses on mandatoryCourses.ProfileId = takenCourses.ProfileID
and mandatoryCourses.CourseID = takenCourses.CourseID
) studentRecords
group by mandatoryCourse.StudentId
having sum(case TakenCourseID when null then 1 else 0 end) = 0
It will give a recordset like the following....
+----------------+-----------------------------+-------------------------------+
| StudentID | CompleteMandatoryCourses | IncompleteMandatoryCourses |
+----------------+-----------------------------+-------------------------------+
| 1 | 15 | 3 |
| 2 | 8 | 0 |
+----------------+-----------------------------+-------------------------------+
If you just want a list of students who have taken all mandatory courses, you could wrap all of the above as shown here...
select studentID
from ( /* insert very long sql here */ )
where IncompleteMandatoryCourses = 0`

multiple conditions in same column with relation

Here is the case:
There is a user table
id email orders_counter
=================================
1 a#a.com 5
2 b#b.com 3
3 c#c.com 0
And a user data table for user's other data
id user_id title value
=======================================
1 1 Name Peter Johnson
2 1 Tel 31546988
3 2 Name Alan Johnson
4 2 Tel 56984887
If I want to fetch all user that
1, orders_counter greater then 3
2, Name contain 'Johnson'
3, Tel contain '88'
AT THE SAME TIME
What is my sql, and if I want to do it rubyonrails way
what is the ActiveRecord code
I know I can it one by one and then join all of them together, but it waste too much resource while conditions build up
Select * From `user` u
inner join `userdata` d on d.user_id=u.id and d.title='Name' and d.value like '%Johnson%'
inner join `userdata` c on c.user_id=u.id and c.title='Tel' and c.value like '%88%'
where orders_counter > 3
the way that you've got your user data table structured, you'll almost always have to join on that table several times in order to "pivot" those values into columns. I'd recommend just creating a table that has name and tel as columns. then the query becomes a lot more simple
select * from `user` u
inner join `user_data` d on d.Tel like '%88%' and d.Name like '%johnson%'
where u.orders_counter > 3
try this one up,
SELECT a.*, b.*
FROM user a
INNER JOIN data b
ON a.id = b.user_ID
WHERE a.orders_counter > 3 AND
(b.title = 'NAME' AND b.value like '%johnson%') AND
(b.title = 'TEL' AND b.tel like '%88%')
try this:
select *
from user U join user_data D
on U.id=D.user_id
where U.orders_counter>3
and D.title='Name' and value like '%Johnson%'
and D.title='Tel' and value like '%88%'