I have a table for referred users (contains an email address and date columns) and a table for users.
I run to get the top referers:
SELECT count(r.Email) as count, r.Email
FROM refs r
WHERE r.referredOn > '2011-12-13'
GROUP BY email
ORDER BY count DESC
But I want to join this with the users table so it displays with other data in the user table, I thought a join would work. Left join becuase emails may be entered incorrectly, some people put first name etc under refs.Email
SELECT count(r.Email) as count, r.Email, u.*
FROM refs r LEFT JOIN users u ON u.email_primary = r.Email
WHERE r.referredOn > '2011-12-13'
GROUP BY email
ORDER BY count DESC
With the above query the count is incorrect, but I don't know why.
Try this one:
SELECT count(r.Email) as count, r.Email
FROM refs r
INNER JOIN users u ON u.email_primary = r.Email
WHERE r.referredOn > '2011-12-13'
GROUP BY email
ORDER BY count DESC
if your adding new column from users u you also need to add it on your group by clause.
Regards
Unfortunately, a LEFT JOIN wont help you here; what this type of join says is give me all the rows in users that match my email, as well as all the rows that have no match on email. If the email doesn't match, then they wont come back as you want.
So you can't use a the left join condition here the way you want.
If you enforced the fact that they had to enter an email everytime, and it was a valid email etc, then you could use an INNER JOIN.
JOINs are usually used to follow referential integrity. So, for example, I have a user with an id in one table, and another table with the column userid - there is a strong relationship between the two tables I can join on.
Jeft Atwood has a good explantion of how joins work.
SEE if this will help you:
SELECT e.count, e.email, u.col1, u.col2 -- etc
FROM (
SELECT count(r.Email) as count, r.Email
FROM refs r
WHERE r.referredOn > '2011-12-13'
GROUP BY email
) e
INNER JOIN
users u ON u.email_primary = e.Email
Instead of a direct join, you could TRY to use your counting query as a subquery-table type..
I wrote this query
SELECT *, count(r.Email) as count FROM refs r
LEFT OUTER JOIN users u ON r.email = u.email_primary
WHERE u.uid IS NOT NULL
GROUP BY u.uid
ORDER BY count DESC
Which showed me that the reason the count was wrong was because some of the email addresses are used twice in the users table (users sharing 'family' email address), this doubled my count, the above query shows each separate user account.
Related
I want to get customer data from all the businesses with more than 1 user.
For this I think I need a subquery to count more than 1 user and then the outer query to give me their emails.
I have tried subqueries in the WHERE and HAVING clause
SELECT u.mail
FROM users u
WHERE count IN (
SELECT count (u.id_business)
FROM businesses b
INNER JOIN users u ON b.id = u.id_business
GROUP BY b.id, u.id_business
HAVING COUNT (u.id_business) >= 2
)
I believe that you do not need a subquery, everything can be achieved in a joined aggregate query with a HAVING clause, like :
SELECT u.mail
FROM users u
INNER JOIN businesses b on b.id = u.id_business
GROUP BY u.id, u.email
HAVING COUNT (*) >= 2
NB : in case several users may have the same email, I have added the primary key of users to the GROUP BY clause (I assumed that the pk is called id) : you may remove this if email is a unique field in users.
I have a really simple table - follow - in which I store followers.
user | following
-----------------
1 | 2
The above means user 1 is following user 2.
I want to display all users on the home page and order them buy who has the most followers, and then return the rest of the users who have no followers. The below query is working as far as displaying the users, but I can't figure out how to retrieve the users who do not have any followers. I've tried RIGHT JOIN users u ON f.following=u.id but that gives me weird results.
This query returns user 2 who has a follower, but doesn't return users 1 and 3, who do not have followers.
Edit: this query is also checking to see if the user is following back, which is why I'm joining using the ID of 1 as a test.
SELECT
u.id
,u.username
,u.avatar
,COUNT(1) AS followers
,ul.*
,fo.*
FROM follow f
LEFT JOIN users u ON f.following=u.id
LEFT JOIN follow fo ON fo.following=u.id AND fo.user=1
LEFT JOIN users_likes ul ON ul.likes=u.id AND ul.user=1
GROUP BY f.following
ORDER BY COUNT(1) DESC
SQL Fiddle: http://sqlfiddle.com/#!2/98f65/1
The problem with your query in the question is that you are left-joining to the follow table. That means that all rows in the follow table are included regardless of their connection to another table. What you want is to show all users, so that is the table that should be on the outer end of the join.
I also think you're trying to do too many things at once here, which is why you're having trouble figuring it out. You want to know who has followers and who doesn't, who's following back, order them, consider the users_likes and so on. I recommend taking a step back and breaking them down into individual queries, and then building those into one result set as needed.
To get the users and number of followers, you can outer join the users table with the follow table like this:
SELECT u.id, u.username, u.avatar, (IFNULL(COUNT(f.following), 0)) AS numFollowers
FROM users u
LEFT JOIN follow f ON f.following = u.id
GROUP BY u.id
ORDER BY numfollowers DESC;
IFNULL is used to check the cases when there are no followers, and no link is made in the outer join so a null value appears.
If you want to work in the users_likes table, you should add it in as another left join. The problem this causes, is that it will return null values for all columns if there are no likes. (Example, if I left join the users_likes table here, I will see null for users 1 and 3 because nobody 'likes' them.) To make the result set a little more understandable, I recommend you don't collect all rows of the users_likes table. Perhaps this query would make more sense:
SELECT u.id, u.username, u.avatar, (IFNULL(COUNT(f.following), 0)) AS numFollowers, ul.user AS likedByUser, ul.created_at
FROM users u
LEFT JOIN follow f ON f.following = u.id
LEFT JOIN users_likes ul ON ul.likes = u.id
GROUP BY u.id
ORDER BY numfollowers DESC;
As far as whether or not a user is following back, I think this would change a bit, as the above only shows the number of followers, and doesn't produce a row for each follower.
Let me know if you have any more questions, here is an SQL Fiddle for the above. I will leave it up to you for handling the null values that occur right now.
You can use an outer join (left or right) from Users to your current query in any number of ways. An easy example that should get you started. This isn't a clean-up up solution, just a dmeo of a way that will work.
SELECT a.*
,b.*
FROM users a
LEFT JOIN (
SELECT
u.id
,u.username
,u.avatar
,COUNT(1) AS followers
FROM follow f
LEFT JOIN users u ON f.following=u.id
LEFT JOIN follow fo ON fo.following=u.id AND fo.user=1
LEFT JOIN users_likes ul ON ul.likes=u.id AND ul.user=1
GROUP BY f.following
) b
ON a.id = b.id
ORDER BY followers DESC
You can do this:
SELECT * FROM (
SELECT u.id, u.username, u.avatar, COUNT(f.user) as followers
FROM users AS u
LEFT JOIN follow AS f ON u.id = f.following
GROUP BY u.id
) AS subselect ORDER BY subselect.followers DESC
I'm trying to get a list of the number of entries in the changes_cc table by each user. Not all users have made entries into it, however for some reason it's returning "1" for each user that has 0 entries. I'm assuming that it's because it's counting the entries in the JOINed table. How can I make it so that it is "0" instead?
SELECT COUNT(*) as num, users.id, realname, username
FROM changes_cc
RIGHT JOIN users
ON changes_cc.user_id = users.id
GROUP BY users.id
I think this should work -- count a specific field in the changes_cc table vs counting *:
SELECT u.id, realname, username, COUNT(c.id) as num
FROM users u
LEFT JOIN changes_cc c
ON u.user_id = c.id
GROUP BY u.id
I prefer reading a LEFT JOIN over a RIGHT JOIN, but they are both OUTER JOINs and work the same.
You should not be using COUNT(*) (counts the record including null values) because it will normally give atleast 1 since it returns all records from the right table. If you specify the column name to be counted, it will gove you the result you want because COUNT only counts for NON_NULL value.
SELECT COUNT(changes_cc.user_id) as num,
users.id,
realname,
username
FROM changes_cc
RIGHT JOIN users
ON changes_cc.user_id = users.id
GROUP BY users.id
Instead of using count(*), use count(changes_cc.user_id).
The problem is that you are counting rows (with the *) rather than counting the non-NULL values in the "right-joined" table.
I have a dilemma.
Let's assume(for simplicity's sake) I have four tables, with different numbers of columns and rows, they are: users, mail, events and service.
When I receive a request, I have an ID that links on three of those tables, but with different columns it matches to.
Let's say, users matches on user_id, mail matches on user_ref and events matches on user_ref as well.
That would've been a fine query for me to write up, even with single, multiple or even all IDs.
The problem arrives on the next step I have to take, and that's the *service table.
The service table doesn't conform to the same standards of the others, thus it does not have an user_id, or user_ref that can be pulled.
What it has instead, is a *mail_ref* column, and it has the potential to contain duplicates.
My current method is trying to use an IN() method, but it only works for selecting a single user/row.
Here's my current query:
SELECT
u.Name as Name,
COUNT(m.user_ref) AS Mail_total,
e.mail_id,
COUNT(e.user_ref) AS Event_total,
COUNT(s.mail_ref) AS service_total
FROM
users u
LEFT JOIN
mail m ON m.user_ref = u.user_id
LEFT JOIN
service s ON s.mail_ref IN(e.mail_id)
LEFT JOIN
events e ON e.user_ref = u.user_id
WHERE u.user_id IN(my,list,of,ids)
GROUP BY s.mail_ref
The problem I have with it currently, is that although it's selecting the correct data, it's not selecting unique data for every id I specify.
It works marginally fine when given a single id, but as mentioned above, not when it has to retrieve multiple rows.
If anyone could help me out it would be much appreciated.
Do a subquery in the left join for service. Instead of:
LEFT JOIN
service s ON s.mail_ref IN(e.mail_id)
Try
LEFT JOIN
(select TOP 1 mail_ref from server) as S on s.mail_ref = e.mail_id
See if that works.
SELECT
u.Name as Name,
(select count(*) from mail m where m.user_ref = u.user_id) AS Mail_total,
e.mail_id,
(select count(*) from events e where e.user_ref = u.user_id) AS Event_total,
(select count(*) from
events e
inner join services s on s.mail_ref = e.mail_id
where
e.user_ref = u.user_id) as service_total
FROM
users u
WHERE u.user_id IN(my,list,of,ids)
The following sql call works fine, returns the correct total retail for customers:
SELECT customer.id,
customer.first_name,
customer.last_name,
SUM(sales_line_item_detail.retail) AS total_retail
FROM sales_line_item_detail
INNER JOIN sales_header
ON sales_header.id = sales_line_item_detail.sales_header_id
INNER JOIN customer
ON customer.id = sales_header.customer_id
GROUP BY sales_header.customer_Id
ORDER BY total_Retail DESC
LIMIT 10
However, i need it to return the customers telephone and email addresses as well.. please keep in mind that not all customers have an email address and telephone number. whenever i left join the email and numbers tables, it throws the total_retail amount off by thousands and I am not sure why.
The following query gives completely wrong results for the total_retail field:
SELECT customer.id,
customer.first_name,
customer.last_name,
IF(
ISNULL( gemstore.customer_phone_numbers.Number),
'No Number..',
gemstore.customer_phone_numbers.Number
) AS Number,
IF(
ISNULL(gemstore.customer_emails.Email),
'No Email...',
gemstore.customer_emails.Email
) AS Email,
SUM(sales_line_item_detail.retail) AS total_retail,
FROM sales_line_item_detail
INNER JOIN sales_header
ON sales_header.id = sales_line_item_detail.sales_header_id
INNER JOIN customer
ON customer.id = sales_header.customer_id
LEFT JOIN gemstore.customer_emails
ON gemstore.customer_emails.Customer_ID = gemstore.customer.ID
LEFT JOIN gemstore.customer_phone_numbers
ON gemstore.customer_phone_numbers.Customer_ID = gemstore.customer.ID
GROUP BY sales_header.customer_Id
ORDER BY total_Retail DESC
LIMIT 10
Any help figuring out why it is throwing off my results is greatly appreciated.
Thanks!
Is it possible that there are multiple records for a Customer_ID in either the customer_emails or customer_phone_numbers tables?
You'll be matching too many records. Try the query without the group by clause and you'll see which ones and how. Most likely the left join's will duplicate order rows on every customer email/phone match.
I am not totally sure, as i can't test this, but the following might be happening.
If there are more than one email or phone number per customer the final result might get multiplied, because of the new joins.
Imagine the query without the group_by and join to sales:
CustomerId Email phoneNumber
1 test#gmx.com 0122233
1 mail#yahoo.com 0122233
The user in this example has 2 mailadresses.
If you would now add the join to sales and the group by, you would have doubled total_retail.
If this should be the case, replacing the LEFT JOIN with an LEFT OUTER JOIN should do the trick. In that case you will however only see the first email/phonenumer of the customer.