How to count number of distinct values when joining multiple tables? - mysql

I have following database tables
categories
id, name
products
category_id, id, product_name, user_id
product_comments
product_id, id, comment_text, user_id
I need a count of number of different users in both products and product_comments tables. I have got the following query where I select all those categories where there is atleast one product and each product may have zero or some comments ... but what I can't figure out is that how to get the sum of these different user ids .. if it were just from one table I would try COUNT(products.user_id) ... .here is my query ..
SELECT
c.*
FROM categories c
INNER JOIN products p ON p.category_id = c.id
LEFT JOIN product_comments pc ON pc.product_id = p.id
I need total number of different users IDs from both products and product_comments tables.
I would expect the result data somewhat like below:
Category_id, Category_name, TotalUsers
1, Test Category, 10
2, Another Category, 5
3, Yet another cat, 3

This will give you an overall count rather than a list of all distinct id's:
SELECT COUNT(DISTINCT user_id)
FROM products
LEFT JOIN product_comments comm ON products.id = comm.product_id

If you want distinct users, then you could try something like:
select distinct user_id from (
select user_id from products
UNION
select user_id from product_comments
) as allusers;
You can then count them:
select count(distinct user_id) from (
select user_id from products
UNION
select user_id from product_comments
) as allusers;

select user_id from products
UNION
select user_id from product_comments
This will give you the distinct list of user ids that exist in the tables. The users could be present in just one of the tables, or both and will still be included in the list.

You can use the DISTINCT keyword and COUNT() function
SELECT DISTINCT
COUNT(c.*)
FROM categories c
INNER JOIN products p ON p.category_id = c.id
LEFT JOIN product_comments pc ON pc.product_id = p.id

Related

Counting number of records from two different tables and grouping them

Table 1: Student(name,enrollment_no,dept,college_id)
Table 2: Faculty(name,faculty_id,dept,college_id)
Desired o/p:
Department, No. of Student, No. of faculty in one table form
query i've been trying :
select
dept,
count(distinct student.dept) as total,
count(distinct faculty.dept) as total1
from student
join faculty
on student.college_id=faculty.college_id
group by dept;
query is not giving any output.
after your help
query is now
select * from (
(select count(*)as stu_count,student.department,student.college_id
from student group by department)T1
inner join
(select count(*)as fact_count,faculty_per.department,faculty_per.college_id
from faculty_per group by department)T2
on T1.college_id=T2.college_id);
Thank you.
select * from ((select count(*)as stu_count,t2.dept,t2.college_id from Student group by dept)T1
inner join
(select count(*)as fact_count,t3.dept,t3.college_id from Facultygroup by dept)T2
on T1.college_id=T2.college_id);
No of students and faculty can be extracted by performing inner join on two separate queries. Distinct is not required as we are doing group by

MySQL - Return the last record on the second table then return all in the first table

I have two tables customers and orders, below is the structure.
Table - contacts
id
Table - orders
id
contact_id
How can I select all from contacts table but only select the latest record from the orders table?
SELECT contacts.*,
Max(orders.id)
FROM contacts
LEFT JOIN orders
ON contacts.id = orders.contact_id
GROUP BY contacts.id;
But I always gets NULL if I use LEFT JOIN, it only have value if I use INNER JOIN.
select the latest record in orders and group it first
select contacts.*, orders.id
from contacts
left join (select max(id) as id, contact_id
from orders
group by contact_id) orders
on contacts.id = orders.contact_id
You can try to use UNION like
select * from orders order by id desc limit 1
UNION
select * from contacts
In order to aggregate max value with all columns from contacts table, add all columns from contacts table after group by function
I trust the answer provided by Alex should work well. The following query shall list all records from contacts and the last id from orders table.
SELECT
c.*,
(SELECT Max(o.id) FROM orders o
INNER JOIN contacts c1 ON o.id=c1.id
)as last_order_id
FROM contacts c

Count for distinct values in union of table one and table two using a subquery

Objective:
I would like to find out the number of unique users who have posted or liked the posts in a particular category.
I have two tables posts and likes with the following fields
posts:
id,
user_id,
category
likes:
id,
user_id,
post_id
Now, I'd like to find distinct user_id values in posts based on a particular category
And distinct user_id values in likes but also the union of both these result sets.
I am using the following Mysql query to achieve this:
SELECT user_id FROM posts WHERE category = 0 union SELECT user_id from likes where likes.post_id in (select id from posts where category = 0)
I'd like to find out if I am getting the desired result AND if there's a better way to achieve this without having to use the sub-query.
You can only speed up the performance by replacing the subquery by INNER JOIN.
SELECT
user_id
FROM posts
WHERE category = 0
UNION
SELECT
likes.user_id
FROM likes
INNER JOIN posts
ON likes.post_id = posts.id
WHERE posts.category = 0;
It will be much faster if likes.post_id is indexed properly and posts.id is PK/indexed.

Mysql most outer table visibility in nested loops

Let's assume i have 4 tables:
'users' (id, username),
'photos' (id, user_id, name),
'photos_comments' (id, photo_id, user_id, text),
'photos_likes' (id, photo_id, user_id, test).
I want to calculate sum of all comments and likes for every user in all of his uploaded photos. For that i'm trying to build a query:
SELECT users.*,
(SELECT SUM(count) as rating FROM(
SELECT COUNT(*) as count FROM photos_likes
WHERE photos_likes.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)
UNION
SELECT COUNT(*) as count FROM photos_comments
WHERE photos_comments.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)
) as total_rating) as rating FROM users
It returns 'Unknown users.id column in WHERE clause' error. So it looks like it can't see users table in most inner query.I can't understand why it happens,because another similar query works ok:
SELECT users.*,
(SELECT COUNT(*) as count FROM photos_likes
WHERE photos_likes.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)) as likes_count,
(SELECT COUNT(*) as count FROM photos_comments
WHERE photos_comments.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)) as comments_count FROM users
In this query it can grab id from users table row in most inner query. Why is it working like that? Thanks for help.
Look into Subqueries in the FROM Clause:
Subqueries in the FROM clause cannot be correlated subqueries, unless used within the ON clause of a JOIN operation.
In your second example, you use the subquery in a where clause. That's the difference.
See also Correlated Subqueries.
select
photos.userid,
photos.photoid,
count(distinct commentid),
count(distinct likeid),
count(distinct commentid) + count(distinct likeid) as total
from
photos
left join photos_comments on photos.photoid=photos_comments.photoid
left join photos_likes on photos.photoid=photos_likes.photoid
group by photos.userid, photos.photoid

MySQL: Left join and column with the same name in different tables

Consider:
SELECT * FROM `product` left join category on product.category_id = category.id
This query works fine. But the problem is, both the product table and the category table have fields named "name" and "id". So when I fetch the result of this query, it gives me only one name and one id, but I want both id's and name's.
How can I do this without having to rename the fields?
Is it possible to return with custom names such as product_name and category_name?
You can add aliases to the fields:
SELECT
a.id,
a.name,
a.category_id,
b.id AS catId,
b.name AS catName
FROM
product AS a
LEFT JOIN
category AS b ON a.category_id = b.category.id
Use the "AS" keyword like:
SELECT product.id AS pid, category.id AS cid ... FROM `product` left join category on product.category_id = category.id
Use aliases with the AS keyword:
SELECT p.id AS product_id, p.name AS product_name, c.id AS cat_id, c.name AS cat_name
FROM `product` AS p
LEFT JOIN category AS c ON p.category_id = c.id
I had a similar problem working with MySQL in a Node.js project.
I found that if you still want to use select * instead of listing out all columns and using an alias, the following works:
SELECT *, category.id AS cId, category.name AS cName
FROM product LEFT JOIN category ON product.category_id = category.id
In other words, just create aliases for the joined columns that have conflicting names. You don't need aliases on other columns or on the table names.
SELECT p.*,c.* FROM product p LEFT JOIN category c on p.category_id = c.id;
Try this:
SELECT product.id AS productid,
category.id AS categoryid, ...
FROM `product` left join category
on product.category_id = category.id
No one has answered it so I am answering it.
If Table 1 has lots of columns like 20 columns and Table 2 has lots of columns like 20 columns. Then it is very tiresome to write a query like table1.a, table1.b
mysqli fetch assoc will return the columns of the right table if the column has same name. Easiest solution I found was to use table1.* in the select
SELECT table1.* FROM table1 LEFT JOIN table2 on table1.id=table2.id
WHERE table1.branch LIKE '%' and table2.branch LIKE 'City'
This will result in columns only from table1, left join and table2 columns were just for where clause.