Mysql update count union based on first table - mysql

I would like to update a table (category) to save how many items are affected to this category (or sub-category from this parent category)
I have this:
update category as a
set nb_items = (select count(distinct item_id) from (
select item_id from item_category where category_id = a.id
union all
select item_category.item_id
from category as sub_cat
inner join item_category
on item_category.category_id = sub_cat.id
where sub_cat.parent_id = a.id
) as tmp
)
where category.parent_id is null
But it complains that a.id is unknown.
I will use a workaround, but I am curious to know how to do this (using a single statement, no temporary tables)
Thank you all!
Best,
EDIT:
Following #Akina 's advice, I got this query, which seemed to work fine!
update category as a
inner join (
select count(distinct item_id) as total, category_id as cat
from item_category
group by cat
union all
select count(distinct item_category.item_id) as total, top_cat.id as cat
from category as top_cat
inner join category as sub_cat
on sub_cat.parent_id = top_cat.id
inner join item_category
on item_category.category_id = sub_cat.id
group by cat
) as tmp
on tmp.cat = a.id
set a.nb_items = tmp.total
where a.parent_id is null;
I'm not 100% sure it removes duplicates, but looking at the results it seems ok.
Thanks again to #Akina !

Related

selecting count(tb2.field_name) based on tb1.id of the table1 and tb2.field

this question might have been asked before, but I could not find. because I was not able to ask correctly all situations. so pardon me if it is a repeated question.
I have 2 tables:
tb1 - categories
tb2 - tasks
I want to select all tasks and quantity of tasks by each category.
What I did is this:
SELECT category_id, category_title,
(SELECT count(task_id) FROM tasks_of_app WHERE category_id ) AS counted_tasks
FROM categories_of_app
but I get all tasks' quantity for each category.
I can't use WHERE because there is not input param.
please, guys help out or tell what book to read to be able to make such kind of queries.
WHERE category_id needs to specify what category_id should be. You need to match it with the value from the other table to make it a correlated subquery.
SELECT category_id, category_title,
(SELECT count(task_id)
FROM tasks_of_app
WHERE tasks_of_app.category_id = categories_of_app.category_id) AS counted_tasks
FROM categories_of_app
But a better way to write this is as a LEFT JOIN between the tables.
SELECT c.category_id, c.category_title, COUNT(t.task_id) AS counted_tasks
FROM categories_of_app AS c
LEFT JOIN tasks_of_app AS t ON c.category_id = t.category_id
GROUP BY c.category_id
If there is a column category_id in tasks_of_app that references the column category_id of categories_of_app then you need a join and aggregation:
SELECT c.category_id, c.category_title, count(t.task_id) counted_tasks
FROM categories_of_app c LEFT JOIN tasks_of_app t
ON t.category_id = c.category_id
GROUP BY c.category_id, c.category_title

mySQL delete row with count(*) GROUP and HAVING

I have this query where i get all double entries with the same articleID and the categoryID = 2153
Now i want to delete this rows. But this seems not to work directly.
So i tried it with subquery, but when i use IN i would need a subquery which returns only the id. But that's also not possible.
How can i delete the rows from this query?
SELECT id, articleID, categoryID, count(*) AS count
FROM `s_articles_categories`
GROUP BY articleID
HAVING count(*) > 1 AND categoryID = 2153
Assuming that the id is the primary key for the table s_articles_categories You could use a join on the subselect result.
To delete all rows :
delete r.*
from s_articles_categories r
INNER JOIN (
SELECT id, articleID, categoryID, count(*) AS count FROM
`s_articles_categories` r
GROUP BY articleID
HAVING count(*) > 1 AND categoryID = 2153
) t on t.id = r.id
If you want to delete all duplicates but keep one of them in each group, then you can use the following query :
DELETE FROM `s_articles_categories` s
WHERE s.categoryID = 2153 AND EXISTS (
SELECT 1
FROM `s_articles_categories` s1
WHERE s1.articleID = s.articleID AND s1.categoryID = s.categoryID AND s1.id < s.id
)
I would recommend writing this as:
delete ac
from s_articles_categories ac join
(select articleId, categoryid, min(id) as min_id
from s_articles_categories
where categoryID = 2153
group by articleId, categoryid
) ac2
on ac2.articleId = ac.articleId and
ac2.categoryid = ac.categoryid and
ac.id > ac2.min_id

Find missing combination of columns from two mysql tables

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).

can we have a query of "10 posts from every category in one request"?

We have a list of posts that belong a category.
can we have a query of "10 posts from every category in one request" ?
Or
We have to query 10 posts for every category separately ?
thx
A fudge involving using GROUP_CONCAT to put all the post ids for a category together (you can add an order clause to this if you want), then substring_index to get the first 10 posts.
This is then joined back to the original table using FIND_IN_SET
SELECT a.*
FROM some_table a
INNER JOIN
(
SELECT category_id, SUBSTRING_INDEX(GROUP_CONCAT(post_id), ',', 10) AS posts
FROM some_table
GROUP BY catgegory_id
) sub0
ON a.category_id = sub0.category_id
AND FIND_IN_SET(a.post_id, sub0.posts)
Or using variables:-
SELECT a.*
FROM some_table a
INNER JOIN
(
SELECT category_id, post_id, #cnt:=IF(#category_id=category_id, #cnt + 1, 1) AS cnt, #category_id:=category_id
FROM
(
SELECT category_id, post_id
FROM some_table
ORDER BY category_id, post_id
) sub0
CROSS JOIN (SELECT #cnt:=0, #category_id:=0) sub1
) sub2
ON a.category_id = sub2.category_id
AND a.post_id = sub2.post_id
AND cnt <= 10
This pseudo code should point you in the right direction.
SELECT DISTINCT CATEGORY FROM T AS T1
CROSS JOIN (SELECT * FROM T WHERE T.CATEGORY = T1.CATEGORY ORDER BY CATEGORY DESC LIMIT 10)

Count query stops working correctly after I add a where clause

I have 3 tables: problems, categories, and a joining table for problem_categories
I am trying to get and count all the categories associated with a problem, and count how many problems are associated with each problem.
Here is my query which doesn't return correct results:
SELECT
category_name ,
categories.category_id ,
problems.problem_id,
COUNT(problems.problem_id) as num_problems
FROM
problem_categories
left JOIN
categories
on
problem_categories.category_id = categories.category_id
left join
problems
on
problem_categories.problem_id = problems.problem_id
WHERE
problem_categories.problem_id = 266
GROUP BY
problems.problem_id , category_name
but as soon as I take out the where clause, it returns all the categories and correct counts of problems. But the trick is that I need to query this for a particular problem_id
Any way to adjust this query to get it right? What am I doing incorrectly here?
Thanks!!
Your question is somewhat unclear. How can you count problems if you are selecting a specific one? If you want to count categories associated with a particular problem you do this (I am assuming you have foreign keys and no duplicates in the problem_categories table):
SELECT problem_id, COUNT(category_id) as num_categories
FROM problem_categories
WHERE problem_id = 266
GROUP BY problem_id
then join to problem table if you need to print out more problem information (for efficiency, always get the detail last). If you are wanting to count problem -> category -> problem then do:
select p1.problem_id, (COUNT(DISTINCT(p2.problem_id)) - 1) as num_problems
FROM
(select problem_id, category_id FROM problem_categories where problem_id = 266) as p1
inner join
(select problem_id, category_id FROM problem_categories) as p2
ON p1.category_id = p2.category_id
GROUP by p1.problem_id
Again if you want to show the problem details, join to the problem table to get them. If you want the num_problems and num_categories together join the first query to the second one before joining to the problem table to get the problem detail. i.e.:
SELECT p1.problem_id, p1.num_categories, p2.num_problems
FROM
(SELECT problem_id, COUNT(category_id) as num_categories
FROM problem_categories
WHERE problem_id = 266
GROUP BY problem_id) as p1
INNER JOIN
(SELECT p1.problem_id, (COUNT(DISTINCT(p2.problem_id)) - 1) as num_problems
FROM
(SELECT problem_id, category_id FROM problem_categories where problem_id = 266) as p1
INNER JOIN
(SELECT problem_id, category_id FROM problem_categories) as p2
ON p1.category_id = p2.category_id
GROUP by p1.problem_id) as p2
ON p1.problem_id = p2.problem_id
I think the following should work:
SELECT category_name , categories.category_id, COUNT(*) as num_problems
FROM problem_categories src JOIN CATEGORIES ON src.category_id = categories.category_id
JOIN problem_categories dest ON categories.category_id = dest.category_id
WHERE problems.problem_id = 266
GROUP BY categories.category_id
What you are doing is for the problem id you want you look up the categories ids and from there the names, and from categories id you look how many (if any) problems each has.
The non usage of the problem table is intentional. Also, for this to perform you need to index the problem_categories in BOTH orders.
UPDATE: The Left join can be an inner join, because each category related to problem 266 has at least one problem (problem 266).
you can prepare the categories and the count of problems in them. after that you can use it as part of any query to supply info.
SELECT categories.category_name ,
categories.category_id,
(select COUNT(*)
from problem_categories
where problem_categories.category_id =categories.category_id
) as num_problems
FROM categories
that can be used in a join (and if you wish stored in a temporary table) to supplement info about the categories:
select problem_categories.problem_id,
temp_tab.category_name ,
temp_tab.category_id,
temp_tab.num_problems
from problem_categories,
(
SELECT categories.category_name ,
categories.category_id,
(select COUNT(*)
from problem_categories
where problem_categories.category_id =categories.category_id
) as num_problems
FROM categories
) temp_tab
where problem_categories.problem_id =266
and problem_categories.category_id = temp_tab.category_id
if you will use this with more than one id or without the id condition, it is better to store the info from the category in a temporary table and join it in order not to repeat the calculations.
SELECT p1.problem_id, problem_categories.category_id , category_name ,
COUNT(*) as num_problems
FROM problems p1
LEFT JOIN problem_categories on
p1.problem_id = problem_categories.problem_id
LEFT JOIN categories on
problem_categories.category_id = categories.category_id
LEFT JOIN problems p2 ON
p2.problem_id = problem_categories.problem_id
WHERE p1.problem_id = 266
GROUP BY categories.category_id,p2.problem_id
ORDER BY categories.category_id
Thats it