Adding Default Values on Joining Tables - mysql

I have the following tables:
Users
user_id course_id completion_rate
1 2 0.4
1 23 0.6
1 49 0.5
... ... ...
Courses
course_id title
1 Intro to Python
2 Intro to R
... ...
70 Intro to Flask
Each entry in the user table represents a course that the user took. However, it is rare that users have taken every course.
What I need is a result set with user_id, course_id, completion_rate. In the case that the user has taken the course, the existing completion_rate should be used, but if not then the completion_rate should be set to 0. That is, there would be 70 rows for each user_id, one for each course.
I don't have a lot of experience with SQL, and I'm not sure where to start. Would it be easier to do this in something like R?
Thank you.

You should first cross join the courses with distinct users. Then left join on this to get the desired result. If the user hasn't taken a course the completion_rate would be null and we use coalesce to default a 0.
select c.course_id,cu.user_id,coalesce(u.completion_rate,0) as completion_rate
from courses c
cross join (select distinct user_id from users) cu
left join users u on u.course_id=c.course_id and cu.user_id=u.user_id

Step1: Take the distinct client_id from client_data (abc) and do 1 on 1 merge with the course data (abc1) . 1 on 1 merge helps up write all the courses against each client_id
Step2: Merge the above dataset with the client info on client_id as well as course
create table ans as
select p.*,case when q.completion_rate is not null then q.completion_rate else 0
end as completion_rate
from
(
select a.client_id,b.course from
(select distinct client_id from abc) a
left join
abc1 b
on 1=1
) p
left join
abc q
on p.client_id = q.client_id and p.course = q.course
order by client_id,course;
Let me know in case of any queries.

Related

How to set up QUERY with different actions based on result value?

I need help on how to set up an QUERY that will result in different outputs based on the results that it achieves on the way and I'm completely stuck!
I'll give you some more details, first of all, here's my current database setup:
#USERS
id username etc..
1 alex123
2 bonnie9
3 clyde_x
#COURSES
id course_name visibility etc..
1 Name 1 1
2 Name 2 0
3 Name 3 1
#COURSE_ENROLMENT
id user_id course_id
1 1 1
2 1 2
3 3 1
The scenario is as following..
I need to list the courses to the users that are enrolled to it, which is quite easily done by something like:
SELECT
*
FROM COURSES C
JOIN COURSE_ENROLMENT E ON C.ID = E.COURSE_ID
However. If the course visibility (Database: Course, Column: visibility) is set to be visible for everyone = 1, then it will override or just ignore the enrolment and show the course to all users anyway.
How can I achieve something like this? I've tried to research CASE but can't really figure out how to proceed. Greatest thanks for any help!
To answer this you're going to need to LEFT JOIN your COURSE and COURSE_ENROLLMENT tables using an OR, so either the person is enrolled in the subject OR the visibility is set to 0.
If you change your JOIN to a LEFT JOIN, that will give you all courses, regardless of whether someone has enrolled in them or not. You can then filter out the courses which have not had anyone enrolled and are not visible by checking for visibility = 1:
SELECT *
FROM Courses C
LEFT JOIN Course_Enrolment E ON C.id = E.course_id
WHERE C.visibility = 1 OR E.id IS NOT NULL
Output:
id course_name visibility id user_id course_id
1 Name 1 1 1 1 1
2 Name 2 0 2 1 2
1 Name 1 1 3 3 1
3 Name 3 1 null null null
Demo on dbfiddle
If visibility=1 means that the course will be returned for all users, then you can do it with a cross join for that case and UNION ALL:
select c.course_name, u.username
from users u
inner join course_enrolment e on e.user_id = u.id
inner join (
select * from courses where visibility = 0
) c on c.id = e.course_id
union all
select c.course_name, u.username
from users u
cross join (
select * from courses where visibility = 1
) c
order by course_name, username

Exclude Staff Members

I have a user table:
id, userID, name, postcode
2 99 Bob AAA BBB
3 8384 Jim CCC DDD
And I have a user-keys table:
id, userID, keyID, val
1 435 3 1
2 773 8 0
3 99 2 1
4 99 5 1
5 99 2 1
Where keyID = 2 indicates that it is a staff member. I want to get all the userIDs from user table that are not staff members. So far I have the following SQL:
SELECT u.`userID` FROM `users` u
WHERE u.`userID` IS NOT NULL AND u.`userID` != 0;
However this does not exclude all users that have an entry in user-keys table with a keyID value of 2 and val of 1.
How can I perform this?
Edit: I neglected to state that any user may have multiple entries in the user-keys table, and I just want one list of userID from the user table, So really the results I am looking for from the above tables would simply be 8384.
You can try the following join query:
SELECT
t1.userID
FROM user t1
LEFT JOIN
(
SELECT DISTINCT userID, keyID -- you don't need DISTINCT here, but it may help
FROM `user-keys` -- and it makes the logic a bit clearer
WHERE keyID = 2
) t2
ON t1.userID = t2.userID
WHERE t2.userID IS NULL
The logic used here is that we retain all users from the user table who do not appear in user-keys with a keyID of 2, or who do not appear in user-keys at all.
Follow the link below for a demo in MySQL. Note that in some databases user-keys would not be a valid table name and would need to be escaped (I escaped above using backticks, SQL Server would use [user-keys], etc.).
Rextester
Update:
If you want to find only staff members, then the logic is much simpler. In this case, we can just INNER JOIN the two tables together and check the status of each user:
SELECT DISTINCT
t1.userID
FROM user t1
INNER JOIN `user-keys` t2
ON t1.userID = t2.userID
WHERE t2.keyID = 2
If you want the user with keyID <> 2 you should use a inner join
SELECT u.userID
FROM users u
and u.userID not in (select userID
from `user-keys`
WHERE keyID <>2 )
You can use below code
SELECT * FROM USER WHERE ID IN
(SELECT ID FROM USER_KEY WHERE KEY_ID <> 2);
If you need all columns you can use,
SELECT * FROM USER U
INNER JOIN USER_KEY UK
ON(U.ID = UK.ID)
WHERE UK.KEYID <> 2;

Construct SQL query to find records that all have the same value

I have a table related to reviews made by a person. The table has the following fields: reviewId, personId, isComplete, where isComplete is a boolean indicating whether the particular person completed his review.
Imagine the following values:
ReviewID | PersonID | isComplete |
1 1 1
2 1 1
3 2 0
4 2 0
5 3 1
6 3 0
In this case I should get only PersonID = 1 as a result because only they have completed all their reviews.
I have tried many queries and the closest one was:
SELECT * FROM reviews x WHERE 1 = ALL (SELECT isComplete FROM reviews y WHERE x.personid = y.personid AND isComplete=1);
Any suggestions or hints will be greatly appreciated.
Table A contains all records
Table B contains all people who have at least 1 outstanding review.
We use a left join and eliminate nulls so that what remains is only users who have records with no outstanding reviews...
.
SELECT Distinct A.PersonID
FROM TABLE A
LEFT JOIN Table B
on A.PersonID = B.PersonId
and B.isComplete = 0
WHERE B.PersonId is null
I used distinct to only return 1 records.
Another way to do this (I believe to be the most efficient) would be to use an exists statement
SELECT Distinct A.PersonID
FROM table A
WHERE not exists (Select 1 from Table B where B.iscomplete=0 and A.PersonID=B.PersonID)
This basically says return all persons who don't have an incomplete review.
The premise in both these cases is that a single entry of an incomplete review is enough to exclude them from the result set.
SELECT DISTINCT(PersonID) FROM reviews
WHERE PersonId NOT IN (
SELECT DISTINCT(PersonID) FROM reviews WHERE isComplete = 0
)
There is more than one way to do this.
SELECT * FROM reviews a WHERE a.PersonId NOT IN
( SELECT b.PersonId FROM reviews b WHERE b.isComplete = 0 )
This is getting all the persons that match isComplete = 0 and then only including the persons that aren't in that list.

Marking Records as duplicates in mySQL

I am not a databases guy,but I have been given the "fun" job of cleaning up someone else's database. We have many duplicate record in our databases and some of customers are getting double or triple billed every month.
Given the following Database example
:
Table: Customers
ID Name Phone DoNotBill
1 Acme Inc 5125551212 No
2 ABC LLC 7138221661 No
3 Big Inc 4132229807 No
4 Acme 5125551212 No
5 Tree Top 2127657654 No
Is it possible to write a query that Identifies the all duplicate phone numbers (in this case records 1 and 4) and then marks and duplicate records yes by updating the DoNotBill column. But leaves the first record unmarked.
In this example case we would be left with:
ID Name Phone DoNotBill
1 Acme Inc 5125551212 No
2 ABC LLC 7138221661 No
3 Big Inc 4132229807 No
4 Acme 5125551212 Yes
5 Tree Top 2127657654 No
something like this?
UPDATE
customers cust,
(SELECT
c1.ID,
c1.name,
c1.phone,
c1.DoNotBill
FROM customers c
LEFT JOIN
(SELECT
cc.ID
FROM customers cc
) as c1 on c1.phone = c.phone
) dup
SET cust.DoNotBill = 'Yes' WHERE cust.id=dup.id ;
To begin with I assume that the DoNotBill column only has two possible values; yes and no. In that case it should be bool instead of varchar, meaning it would be either true or false.
Furthermore I don't get the meaning of the DoNotBill column. Why wouldn't you just use something like this?
select distinct phone from customers
SQL SELECT DISTINCT
That would give you the phone numbers without duplicates and without the need for an extra column.
This depends on ur data amount
You can do it in steps and make use some tools like excel...
This qrt
SELECT a.id,b.id,a.phone FROM clients a , clients b WHERE
A.phone =b.phone
And a.id!=b.id
The result is all duplicated records.
Add
Group by a.phone
And u will get 1 record for each 2 duplicates.
if you like the records and they are whT u need. ChNge select to select a.id and
Use this qry as subqry to an update sql statement
UPDATE clients SET billing='no' WHERE id IN ( sql goes here)
UPDATE customers c SET c.DoNotBill="Yes";
UPDATE customers c
JOIN (
SELECT MIN( ID ) ID, Phone
FROM customers
GROUP BY Phone
) u ON c.ID = u.ID AND c.Phone = u.Phone
SET c.DoNotBill="No";
That way not only duplicates are eliminated, but all multiple entries are dealt with.

MySQL: Join based on multiple columns

I need to join table_1 and table_2 in MySQL and compare which user has the most winnings. Then update table_2.winner with the user id which has won..
table 1
city user winnings
1 a 99
1 b 0
1 c 50
1 d 2
table 2
city user_1 user_2 winner
1 a b a
1 c d 50
However I'm struggling to figure out how to join the tables thus far I have
SELECT table_1.winnings AS win_a, table_1.winnings AS win_b
FROM table_1, table_2
WHERE table_2.user_1 = table_1.user
AND table_2.user_2 = table_1.user
http://sqlfiddle.com/#!2/c855b/1
You can join against the table multiple times like this:
SELECT IF(user1.winnings > user2.winnings, "user1", "user2")
FROM table_2 games
JOIN table_1 user1 ON games.user_1 = user1.user
JOIN table_1 user2 ON games.user_2 = user2.user
http://sqlfiddle.com/#!2/c855b/16
I just used #skishore's query, a bit fixed, because it is broken for draws. The one that takes draws under consideration would be
SELECT
case when user1.winnings > user2.winnings then user1.user
when user2.winnings > user1.winnings then user2.user
else null
end
FROM table_2 games
JOIN table_1 user1 ON games.user_1 = user1.user
JOIN table_1 user2 ON games.user_2 = user2.user
But apart from this, I still don't get the purpose. I wrote this in comment to #skishore answer, but paste it here also. Consider the case:
User c won 99 matches played with user b, user d on the other hand won 2 matches played with user c. But who will be the winner between c and d? C
The second question is - why do you need this stored in a separate table? Winnings number will be dynamically changing so you would have to create trigger to keep winner column on table2 up to date. Can't you just get winner using this query?