Multiple Left Joins - mysql

I'm trying to create a single query containing optional aggregate values from multiple tables and having some difficulty. I'm going to try to simplify my problem to as few fields as possible.
Tables
applications
appid | pageid
contests
id | appid | winnerid
signups
id | contestid | firstname | lastname
referrals
id | signupid
tickets
id | signupid
Purpose of Query
I'm trying to combine the applications and contests tables based on the pageid parameter, join the signups table to get the winner when available, and then a count of all signups, referrals, and tickets or simply a zero value when none is available.
What I've got so far
SELECT t1.*, `Winner`.Name AS Winner, IFNULL(`srt`.Signups,0) AS Signups,
IFNULL(`srt`.Referrals,0) AS Referrals,
IFNULL(`srt`.Tickets,0) AS Tickets
FROM applications a, (contests c LEFT JOIN
/* Join signups table to retrieve winner's first/last name */
(SELECT id, contestid, CONCAT(firstname, ' ' , lastname) AS Name
FROM signups) `Winner`
ON c.winnerid = `Winner`.id
AND c.contestid = `Winner`.contestid) LEFT JOIN
/* Join signups, referrals, and tickets to retrieve counts */
(SELECT s.signupid, COUNT(*) AS Signups, Referrals, Tickets
FROM (signups s LEFT JOIN
(SELECT r.signupid, COUNT(r.id) AS Referrals
FROM signups s, referrals r
WHERE s.signupid = r.signupid) `Referrals`
ON s.signupid = `Referrals`.signupid) LEFT JOIN
(SELECT t.signupid, COUNT(t.id) AS Tickets
FROM signups s, tickets t
WHERE s.signupid = t.signupid) `Tickets`
ON s.signupid = `Tickets`.signupid) `srt`
ON signupid = `srt`.signupid
WHERE a.id = c.applicationid
AND a.pageid = #pageid
ORDER BY c.id IN (SELECT id FROM contests WHERE active = 1) desc, c.addeddate desc;
Problem
I'm positive this isn't the most efficient way to do what I'm trying to do and regardless it doesn't work. The values for the signups/referrals/tickets simply returns a count for every record in each of the tables. The winner join works fine, and if I limit it to just the signups, that works as well. The signups table is linked to the contests table, and then the referrals and tickets are related directly to the signups table. Any help on this complex query would be greatly appreciated.

Forgive me if I am misunderstanding, and also for my lack of knowledge of differences between MSSQL and mysql, but I would try something like this:
SELECT
( SELECT CONCAT(IFNULL(firstname,''), ' ' , IFNULL(lastname,''))
FROM Signups
WHERE id = c.winnerid) as Winner,
( SELECT COUNT(*)
FROM Signups
WHERE contestid = c.id) as Signups,
( SELECT COUNT(*)
FROM referrals r LEFT JOIN signups su ON r.signupid = su.id
WHERE su.contestid = c.id) as Referrals,
( SELECT COUNT(*)
FROM tickets t LEFT JOIN signups su ON t.signupid = su.id
WHERE su.contestid = c.id) as Tickets
FROM
Contests c

Related

SQL LEFT JOIN for all records of first table and matched records of another table

I have 3 tables.
Table seller with columns like id, name etc.
Table customer like id, name etc.
Table connections which have seller_id, customer_id, status of friendship like "friends", "pending_request" etc.
Now I want to get all the sellers who are not friends of a specific customer.
So I tried like fetching records from seller table with left join of connections table, with condition status is not "friends"
I tried the following query but didn't help me. I also tried other queries but didn't help.
SELECT * FROM `seller` LEFT JOIN `connections` ON seller.user_id = connections.user_id WHERE customer_id = 10 AND request_status NOT LIKE "friends"
Here is the reference screen I want the result. Like for a particular customer, all the sellers who are not friends or request is pending.
Join the connections of type 'friends' for customer_id = 10 and in a WHERE clause check for the connections.user_id being NULL, i.e. nothing has been joined.
SELECT *
FROM seller
LEFT JOIN connections
ON seller.user_id = connections.user_id
AND connections.customer_id = 10
AND connections.request_status = 'friends'
WHERE connections.user_id IS NULL;
Or use a correlated subquery, that gets the connection with a NOT EXISTS.
SELECT *
FROM seller s
WHERE NOT EXISTS (SELECT *
FROM connections c
WHERE c.user_id = s.user_id
AND c.customer_id = 10
AND c.request_status = 'friends');
Try this:
SELECT * FROM `seller` LEFT JOIN `connections` ON seller.user_id = connections.user_id WHERE customer_id = 10 AND (request_status NOT LIKE "friends"
or request_status is null)
Or this:
SELECT * FROM `seller` LEFT JOIN `connections` ON seller.user_id = connections.user_id WHERE customer_id = 10 AND IFNULL(request_status,"other") NOT LIKE "friends"

MySQL - Marking duplicates from several table fields, as well as data from another table

I have two tables - one shows user purchases, and one shows a product id with it's corresponding product type.
My client wants to make duplicate users inactive based on last name and email address, but wants to run the query by product type (based on what type of product they purchased), and only wants to include user_ids who haven't purchased paint (product ids 5 and 6). So the query will be run multiple times - once for all people who have purchased lawnmowers, and then for all people who have purchased leafblowers etc (and there will be some overlap between these two). No user_id that has purchased paint should be made inactive.
In terms of who should stay active among the duplicates, the one to stay active will be the one with the highest product id purchased (as products are released annually). If they have multiple records with the same product id, the record to stay active will be the one with most recent d_modified and t_modified.
I also want to shift the current value of 'inactive' to the 'previously_inactive' column, so that this can be easily reversed if need be.
Here is some sample table data
If the query was run by leafblower purchases, rows 5, 6, and 7 would be made inactive. This is the expected output:
If the query was run by lawnmower purchases, rows 1 and 2 would be made inactive. This would be the expected output:
If row 4 was not the most recent, it would still not be made inactive, as user_id 888 had bought paint (and we want to exclude these user_ids from being made inactive).
This is an un-optimised version of the query for 'leafblower' purchases (it is working, but will probably be too slow in the interface):
UPDATE test.user_purchases
SET inactive = 1
WHERE id IN (
SELECT z.id
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
AND id NOT IN (
SELECT MAX(z.id)
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
GROUP BY surname, email
)
)
Any suggestions on how I can streamline this query and optimise the speed of it would be much appreciated.

query left join or data insert?

OK - I have three tables, structured below
tbl_1
------
userid
teamid
teamname
eliminated
tbl_2
------
teamid
week
team
tbl_3
------
team
NFLname
nfl-schedule
------
week
time
awayteam (same as "team" in tbl_3)
hometeam (same as "team" in tbl_3)
This is a query on a "survivor league" where I need to get the teamid, teamname, eliminated, team, week, and NFLname for each week. If a user hasn't selected a team for this week, week 2 for instance, I want to see a blank row for that week. I"m assuming I could backfill the rows into the database for each teamid, but was wondering if I could do this simply with sql and some inner joins?
select a.teamid, a.teamname, a.eliminated, b.team, b.week, c.NFLnamefrom `tbl_1` a
left join `tbl_2` b on a.teamid = b.teamid
left join `tbl_3` c on c.`team` = b.team
where a.userid = XXX
You can use left joins for this, but you have to take care of the order you use the tables.
Another problem would be when there is a pick for this week from another user, so I think you would have to use a subquery like this:
select w.week,t.teamid,t.teamname,t.eliminated,t.team,t.NFLname
from nfl-schedule w
left join (
select a.teamid, a.teamname, a.eliminated, b.team, b.week, c.NFLname
from `tbl_1` a
left join `tbl_2` b on a.teamid = b.teamid
left join `tbl_3` c on c.`team` = b.team
where a.userid = XXX
) t on w.week = t.week

WHERE value IS NOT IN (subquery)

I've been struggling with this query.
I have two tables. One with coupons and Invoicenumbers. One with Invoicenumbers and customer names.
I need to get the customers who have not used a given coupon.
Here are the tables:
Promotion table:
Promotions
Invoice | Coupon
----------------
1 | couponA
2 | couponB
3 | couponB
Orders Table:
Orders
Invoice | Customer
------------------
1 | Jack
2 | Jack
3 | Jill
So Jack has used coupons A and B. And Jill has only used coupon B.
If my query were select customers who have not used coupon A, I should get Jill.
This works, but it seems clumsy and slow. Is there a better way?
SELECT Customer
FROM Promotions INNER JOIN Orders
ON Promotions.Invoice = Orders.Invoice
WHERE Customer NOT IN(
SELECT Customer
FROM Promotions INNER JOIN Orders
ON Promotions.Invoice = Orders.Invoice
WHERE Coupon = couponA)
GROUP BY Customer
Thanks for looking!
edit:
Here's an SQLFiddle schema
http://sqlfiddle.com/#!2/21d31/6
Updated: We should use prefer to use joins for better performance when its easy to do for us. Join vs. sub-query
Sql Fiddle
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders o2
join
(
Select distinct invoice from Promotions where Coupon='couponA'
) t3
on o2.invoice = t3.invoice
) t2
on o.customer != t2.changedname;
Note: I changed column name customer for t3 because two joined tables must have different column names
Explanation:
Using inner or sub query is expensive when you have big data. use joins instead, lets learn converting subquery to join
With Subquery We had:
Select distinct Customer from orders where customer not in
(SELECT distinct Customer FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA'));
Converting sub-query to join
First step:
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA')
) t2
on o.customer != t2.changedname;
2nd step:
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders o2 where invoice
join
(
Select distinct invoice from Promotions where Coupon='couponA'
) t3
on o2.invoice = t3.invoice
) t2
on o.customer != t2.changedname;
And that's it, much faster for tables having numerous rows
Original answer:
Use not in. Have a look.
Select distinct Customer from orders where customer not in
(SELECT distinct Customer FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA'));
Edit I have added distinct to make query faster
SQL Fiddle
SELECT DISTINCT o2.customer FROM ORDER o2
LEFT JOIN (promotions p1
JOIN Orders o1 ON p1.cuopon = 'CuoponA' AND p1.invoice = o1.invoice ) p3
ON o2.customer = p3.customer
WHERE p3.customer IS NULL
Try this query instead:
SELECT DISTINCT Customer
FROM Orders o1
WHERE NOT EXISTS (
SELECT 1
FROM Orders o2
INNER JOIN Promotions ON Promotions.Invoice = o2.Invoice
WHERE o1.Customer = o2.Customer AND Coupon = 'couponB')
The idea is to get rid of the GROUP BY by removing a join in the top part of the query, and also eliminate the NOT IN by making a coordinated subquery.
Here is a link to sqlfiddle.
Try this with a right join
SELECT Customer, Coupon
FROM Promotions
RIGHT JOIN Orders ON Promotions.Invoice = Orders.Invoice
AND Coupon = 'couponA'
GROUP BY Customer
HAVING Coupon IS NULL

Join two queries together

I have a database that holds a bunch of student information in the following tables
Student:
id
created_on
updated_on
StudentHistory
id
studentid
schoolid
gradeid
datestamp
active
The student history manages how students change over the years and allows me to report back student growth.
I now need to do the following query:
SELECT * FROM students
INNER JOIN studenthistory
ON student.id = studenthistory.studentid
WHERE studenthistory.active = 1
The problem is that the query won't necessarilly pull the latest history record like I need. Is there a way that I can guarentee that only the latest history record will get pulled in the join?
SELECT *
FROM students s
JOIN studentshistory sh
ON sh.id =
(
SELECT shi.id
FROM studentshistory shi
WHERE shi.studentid = s.id
AND shi.active = 1
ORDER BY
studentid DESC, active DESC, datestamp DESC, id DESC
LIMIT 1
)
Create an index on studentshistory (studentid, active, datestamp, id) for this to work fast.
I think this should work:
SELECT * FROM Students
INNER JOIN StudentHistory sh1
ON Student.studentid = sh1.studentid
AND StudentHistory.datestamp =
(SELECT MAX(datestamp)
FROM StudentHistory sh2
WHERE sh2.studentid = sh1.studentid);