Let's say I'm having three tables as follows:
PRODUCT table (P) has ID, NAME
CATEGORY table (C) has ID, NAME
RELATION table (R) has ID, PRODUCT_ID, CATEGORY_ID
I'm currently on the product list page and I want a function to be able to search products by its category name.
select P.*
from P
where P.id in (select group_concat(distinct R.product_id) from C join R on C.id=R.category_id where C.name like '%something%')
The above SQL will only give me the first match, but running the query in the brackets will return more than one id. How do I correct this?
The group_concat() is unnecessary:
select P.*
from P
where P.id in (select R.product_id
from C join
R
on C.id = R.category_id
where C.name like '%something%'
);
Your query is fine, but it would more typically be written as:
select P.*
from P join
R
on P.id = R.product_id join
C
on C.id = R.category_id
where C.name like '%something%';
This version could return duplicates, if more than one category matched the like condition.
Related
I am making a forum page using MySQL as database, but I'm coming from MongoDB and am a bit confused. When I fetch all posts for a specific category it looks something like this
SELECT p.id, p.posted_at, p.title, p.content, c.name AS category_name, u.name
AS author_name
FROM posts AS p
INNER JOIN users AS u ON p.author = u.id
INNER JOIN categories AS c ON p.category = c.id
WHERE p.category = 3 <-- Category ID
People can follow posts so I have a table called user_post_relations which contains two columns; user_id and post_id.
My goal is to add a follower count per post to the query that's getting all the posts per category. How can this be achieved with only one query?
Add the following JOIN with sub-query to your query
JOIN (SELECT post_id, COUNT(*) follower_count
FROM user_post_relations
GROUP BY post_id) AS upr ON upr.post_id = p.id
And then add upr.follower_count to your SELECT list
I am counting the results of a table I LEFT JOINED:
SELECT p.*,COUNT(po.name) AS posts
FROM projects p
left join posts po on p.name = po.name
group by p.id
http://sqlfiddle.com/#!9/3e9d4b/4
But now I want to add another table via LEFT JOIN and count it also:
SELECT p.*,COUNT(po.name) AS posts,
COUNT(ta.name) AS tasks
FROM projects p
left join posts po on p.name = po.name
left join tasks ta on p.name = ta.name
group by p.id
http://sqlfiddle.com/#!9/ee068/2
But now the counting is wrong. For cat I have only 2 posts and 3 tasks. Where is the number 6 coming from?
Left joins are not the right tool for this. You should use subselects:
SELECT p.*,
(SELECT COUNT(*) FROM posts po WHERE p.name = po.name) AS posts,
(SELECT COUNT(*) FROM tasks ta WHERE p.name = ta.name) AS tasks
FROM projects p
You can still use joins, but instead of a single top level aggregation, you can aggregate each of the two tables in separate subqueries:
SELECT
p.name,
COALESCE(t1.posts_cnt, 0) AS posts_cnt,
COALESCE(t2.tasks_cnt, 0) AS tasks_cnt
FROM projects p
LEFT JOIN
(
SELECT name, COUNT(*) AS posts_cnt
FROM posts
GROUP BY name
) t1
ON p.name = t1.name
LEFT JOIN
(
SELECT name, COUNT(*) AS tasks_cnt
FROM tasks
GROUP BY name
) t2
ON p.name = t2.name
Your current queries have problems, because you are aggregating by id but selecting other columns. From what I see, you want to be aggregating in all three tables using the name column.
This approach should outperform an approach using correlated subqueries.
I have one product table like this:
productid categoryid
and another category table like this:
categoryid parentid
would like to find rows missing from product table
select distinct c.parentid, pc.productid, from products_categories pc
join categories c on pc.categoryid = c.categoryid
where concat(pc.productid,'-',c.parentid) not in (
select distinct concat(productid,'-',categoryid) from products_categories
)
however this is extremely slow. is there a way to do this with joins instead of the not in concat line? the concat is used to account for all possible combos.
Everytime you are adding a prefix productID & - to either parentID or categoryID.
You can try this:
SELECT DISTINCT c.parentid, pc.productid
FROM products_categories pc
JOIN categories c on pc.categoryid = c.categoryid
WHERE c.parentid NOT IN (
SELECT DISTINCT categoryid FROM products_categories
)
Edit 1: doesnt account for all combos
SELECT DISTINCT c1.parentid, pc.productid
FROM products_categories pc
INNER JOIN categories c1 ON pc.categoryid = c1.categoryid
LEFT JOIN categories c2 ON c1.parentid = c2.categoryid
WHERE c2.categoryid IS NULL
To find what is in tbl_x but not in tbl_y, do this
SELECT ...
FROM tbl_x
LEFT JOIN tbl_y USING(...)
WHERE tbl_y.id IS NULL;
The construct IN ( SELECT ... ) is very poorly optimized. LEFT JOIN is well optimized (assuming suitable index).
I have 2 tables:
Customers:
- ID
- NAME
Modulemembers:
- ID
- customer_id
- enabled
I use this to enable a function of my site for some customers.
I have a form where the admin of the site can add the module for a customer, so i need a query that looks for customers that are NOT member of the modulemembers table.
I made this:
SELECT customers.id,
customers.name
FROM customers,
modulemembers
WHERE customers_id != modulemembers.cust_id
ORDER BY customers.name
but it does not work. What am I doing wrong?
You can use NOT EXISTS in the WHERE clause to get the result:
SELECT c.id,
c.name
FROM customers c
WHERE not exists (select customer_id
from modulemembers m
where c.id = m.customer_id)
order by c.name
You can use a LEFT OUTER JOIN and filter based on NULLs. The query below will pull in all results from the customers table and the modulemembers table. If there is not a match in the modulemembers table then the custid will be NULL.
SELECT c.id, c.name
FROM customers c
LEFT OUTER JOIN modulemembers m ON c.customers_id = m.cust_id
WHERE m.custid IS NULL
ORDER BY c.name
Try something like this:
SELECT c.ID AS customerId,
c.NAME AS fullName,
m.ID AS memberId
FROM Customers AS c
LEFT JOIN Modulemembers AS m ON m.customer_id = c.ID
WHERE m.ID IS NULL;
Hi i'm struggling to write a particular MySQL Join Query.
I have a table containing product data, each product can belong to multiple categories. This m:m relationship is satisfied using a link table.
For this particular query I wish to retrieve all products belonging to a given category, but with each product record, I also want to return the other categories that product belongs to.
Ideally I would like to achieve this using an Inner Join on the categories table, rather than performing an additional query for each product record, which would be quite inefficient.
My simplifed schema is designed roughly as follows:
products table:
product_id, name, title, description, is_active, date_added, publish_date, etc....
categories table:
category_id, name, title, description, etc...
product_category table:
product_id, category_id
I have written the following query, which allows me to retrieve all the products belonging to the specified category_id. However, i'm really struggling to work out how to retrieve the other categories a product belongs to.
SELECT p.product_id, p.name, p.title, p.description
FROM prod_products AS p
LEFT JOIN prod_product_category AS pc
ON pc.product_id = p.product_id
WHERE pc.category_id = $category_id
AND UNIX_TIMESTAMP(p.publish_date) < UNIX_TIMESTAMP()
AND p.is_active = 1
ORDER BY p.name ASC
I'd be happy just retrieving the category id's releated to each returned product row, as I will have all category data stored in an object, and my application code can take care of the rest.
Many thanks,
Richard
SELECT p.product_id, p.name, p.title, p.description,
GROUP_CONCAT(otherc.category_id) AS other_categories
FROM prod_products AS p
JOIN prod_product_category AS pc
ON pc.product_id = p.product_id
LEFT JOIN prod_product_category AS otherc
ON otherc.product_id = p.product_id AND otherc.category_id != pc.category_id
WHERE pc.category_id = $category_id
AND UNIX_TIMESTAMP(p.publish_date) < UNIX_TIMESTAMP()
AND p.is_active = 1
GROUP BY p.product_id
ORDER BY p.name ASC
You would use an inner join to the product_category table, doing a left join there is pointless as you are using the value from it in the condition. Then you do a left join on the product_category table to get the other categories, and join in the categories for the data:
select
p.product_id, p.name, p.title, p.description,
c.category_id, c.name, c.title
from
prod_products p
inner join prod_product_category pc on pc.product_id = p.product_id
left join prod_product_category pc2 on pc2.product_id = p.product_id
left join prod_categories c on c.category_id = pc2.category_id
where
pc.category_id = #category_id and
unix_timestamp(p.publish_date) < unix_timestamp() and
p.is_active = 1
order by
p.name