select count items each group - mysql

I have two tables category and adverts, i need to select all categories and the number of adverts it has in the adverts table that has at least count greater than zero
the category table
cat_id | Name
----------------
1 | Toys
2 | fashion
3 | electronics
The adverts table
cat_id | title
----------------
1 | a
2 | b
2 | c
1 | d
2 | e
what i expect
cat_id | count | Name
-----------------------
1 |2 | a
2 |3 | b
The query i tried
Select
c.name, c.cat_id,c.parent_id, #count:= (Select Count(av.cat_id) From adsview av Where av.cat_id = c.cat_id)
from
category c WHERE #count > 0
i am getting and empty result, what am i doing wrong?

If you want to make sure that the cat_id from category table are in adverts table you need to join as
select
c.cat_id,
c.Name,
count(a.cat_id) as `count`
from category c
join adverts a on a.cat_id = c.cat_id
group by c.cat_id ;

select cat_id, count(*)
from adverts
group by cat_id;
So the mySQL query engine will grab every single row from the adverts table, it'll put them into neat piles where all rows in the pile have the same category, it'll count the number of rows in each pile, and then it'll return to you a result row for each pile with the pile's id and the number of rows.
Now lets add something: we want to also get the category's name. So we indicate that in the select clause, and add a join to the from clause. The join says "for every row in table a, consider it alongside every row in table b. if some condition holds, put this combined row into the from set". You can see that joins are actually quite slow in SQL (relatively).
select c.cat_id, count(*) as count, c.name
from adverts as a join categories as c on a.cat_id = c.cat_id
group by c.cat_id;
Note also that I've aliased the tables as a and c respectively, so as to remove the ambiguity over the column name cat_id (otherwise the mySQL query engine may get confused).

You can try this, mate:
SELECT
c.cat_id,
COUNT(a.cat_id) AS count,
a.title
FROM
category c
LEFT JOIN adverts a ON a.cat_id = c.cat_id
GROUP BY
c.cat_id
HAVING
count > 0;
or this:
SELECT
c.cat_id,
COUNT(a.cat_id) AS count,
a.title
FROM
category c
INNER JOIN adverts a ON a.cat_id = c.cat_id
GROUP BY
c.cat_id;

You have to use group by function like below
select cat_id, count(*) as count
from adverts
group by cat_id;

Related

How to execute group by in another group by in mysql?

I have 2 tables: cars and lands:
cars (id, land_id)
lands (id, district_id, location)
I need to get count of cars per each district. What I have now:
SELECT district_id, COUNT(*)
FROM lands
GROUP BY district_id
But of course it returns only count of lands per each district. How to get count of cars?
Expected result:
district_id | cars_count
1 | 30
3 | 10
...
The problem that you select only from lands table, when you also need to join cars table.
SELECT l.id, count(c.land_id)
FROM lands as l LEFT JOIN cars as c ON l.id = c.land_id
GROUP BY l.id
left join these 2 tables based on land_id(cars) and id(lands)
select a.id,
count(b.id)
from lands a
left join cars b on a.id=b.land_id
group by 1;

how to get result of join also for records that dont have a joined record with another table?

I have this query:
SELECT suppliers.id, count(*)
FROM suppliers
INNER JOIN supplier_addresses
ON suppliers.id = supplier_addresses.supplier_id
GROUP BY suppliers.id;
this gives my a table of supplierId and count of its addresses in the supplier_addresses table. But it only shows me suppliers that have at least 1 address.
I want to see in the result also count of 0 addresses...for example:
supplier.id | count(*)
1 3
2 0
3 1
4 9
in my query I dont see the second record.
Use LEFT JOIN
SELECT suppliers.id, count(supplier_addresses.supplier_id )
FROM suppliers
LEFT JOIN supplier_addresses
ON suppliers.id = supplier_addresses.supplier_id
GROUP BY suppliers.id;

MySQL - Search for a product by many categories (many rows...) faster?

I have a large set of data (ie 20 milions row of each table) which looks kind of like this (I'm not really dealing with products and catogories, but the same kind of situation)
Table products
|id|name|created_at|
--------------------
|1 |....|2018-06...|
|2 |....|2018-06...|
|3 |....|2018-06...|
|4 |....|2018-06...|
|5 |....|2018-06...|
...etc.
And a table of categories
|id|product_id|category|description|
------------------------------------
|1 |1 | abc | def |
|2 |1 | ghi | jkl |
|3 |1 | mno | pqr |
|4 |2 | abc | stu |
|5 |2 | wvx | yz |
...etc
What I want to do is a search, to find products with many categories, ie.
SELECT DISTINCTROW * FROM product WHERE
product.id in (
SELECT categories.product_id FROM categories WHERE category = 'abc'
)
AND
product.id in(
SELECT categories.product_id FROM categories WHERE category = 'ghi'
)
AND
product.id in(
SELECT categories.product_id FROM categories WHERE category= 'mno'
)
AND 'some extra where' ORDER BY product.created_au LIMIT 10 offset 0
But this is really slow... I've tried different approaches on this, but everyone takes at least 30 seconds.
I've made index of the columns used for joining.
So basicly I want to do a search where a product have to match one or many categories. Also, later on, I want to search so that a product matches a category and a description (might be from different rows in categories table).
Any ideas? Or perhaps links where I can read more about cases like this?
A temporary table should help speeding up things.
CREATE TEMPORARY TABLE tmpCategories( category_id INT)
Find a way to insert the ids of the category that you want to search for into this table.
Then write a join similar to this:
SELECT p.* FROM product p
INNER JOIN categories c ON p.product_id = c.product_id
INNER JOIN tmpCategories tc ON tc.catrgory_id = c.category_id
Your query might not be exactly like this. But this approach may speed things up.
P.S. I'm typing from my phone so pardon the formatting :)
First suggestion you could use INNER JOIN on subquery instead of IN
SELECT DISTINCTROW *
FROM product p
INNER JOIN (
SELECT categories.product_id FROM categories WHERE category = 'abc'
) t1 on p.id = t1.product_id
INNER JOIN (
SELECT categories.product_id FROM categories WHERE category = 'ghi'
) t2 on p.id = t2.product_id
INNER JOIN (
SELECT categories.product_id FROM categories WHERE category= 'mno'
) t3 p.id = t3.product_id
WHERE 'some extra where'
ORDER BY product.created_au LIMIT 10 offset 0
You could also try using a single subquery for obtain all the product_id with the 3 category
SELECT DISTINCTROW *
FROM product p
INNER JOIN (
SELECT categories.product_id FROM categories WHERE category IN ( 'abc','ghi', 'mno')
group by categories.product_id
having count(distinct category ) = 3
) t1 on p.id = t1.product_id
WHERE 'some extra where'
ORDER BY product.created_au LIMIT 10 offset 0
If the inner join doesn't speed up as expected, then be sure you have proper composite index on categories table
CREATE INDEX index_name ON categories(category,product_id );
I would simply use GROUP BY and HAVING to get the products:
select c.product_id
from categories c
where c.category in ('abc', 'ghi', 'mno')
group by c.product_id
having count(*) = 3;
You can use join, exists, or in to get the rest of the product information:
select p.*
from products p join
(select c.product_id
from categories c
where c.category in ('abc', 'ghi', 'mno')
group by c.product_id
having count(*) = 3
) cp
on c.product_id = p.i
where . . . -- other conditions on product

How to count the number of instances of each foreign-key ID in a table?

Here's my simple SQL question...
I have two tables:
Books
-------------------------------------------------------
| book_id | author | genre | price | publication_date |
-------------------------------------------------------
Orders
------------------------------------
| order_id | customer_id | book_id |
------------------------------------
I'd like to create a query that returns:
--------------------------------------------------------------------------
| book_id | author | genre | price | publication_date | number_of_orders |
--------------------------------------------------------------------------
In other words, return every column for ALL rows in the Books table, along with a calculated column named 'number_of_orders' that counts the number of times each book appears in the Orders table. (If a book does not occur in the orders table, the book should be listed in the result set, but "number_of_orders" should be zero.
So far, I've come up with this:
SELECT
books.book_id,
books.author,
books.genre,
books.price,
books.publication_date,
count(*) as number_of_orders
from books
left join orders
on (books.book_id = orders.book_id)
group by
books.book_id,
books.author,
books.genre,
books.price,
books.publication_date
That's almost right, but not quite, because "number_of_orders" will be 1 even if a book is never listed in the Orders table. Moreover, given my lack of knowledge of SQL, I'm sure this query is very inefficient.
What's the right way to write this query? (For what it's worth, this needs to work on MySQL, so I can't use any other vendor-specific features).
Your query is almost right and it's the right way to do that (and the most efficient)
SELECT books.*, count(orders.book_id) as number_of_orders
from books
left join orders
on (books.book_id = orders.book_id)
group by
books.book_id
COUNT(*) could include NULL values in the count because it counts all the rows, while COUNT(orders.book_id) does not because it ignores NULL values in the given field.
SELECT b.book_id,
b.author,
b.genre,
b.price,
b.publication_date,
coalesce(oc.Count, 0) as number_of_orders
from books b
left join (
select book_id, count(*) as Count
from Order
group by book_id
) oc on (b.book_id = oc.book_id)
Change count(*) to count(orders.book_id)
You're counting the wrong thing. You want to count the non-null book_id's.
SELECT
books.book_id,
books.author,
books.genre,
books.price,
books.publication_date,
count(orders.book_id) as number_of_orders
from books
left join orders
on (books.book_id = orders.book_id)
group by
books.book_id,
books.author,
books.genre,
books.price,
books.publication_date
select author.aname,count(book.author_id) as "number_of_books"
from author
left join book
on(author.author_id=book.author_id)
GROUP BY author.aname;

SELECT all items in common between two users on three tables

I have three tables
item_to_user (to store the relations between user and item)
| item_to_user_id | user_id | item_id |
-----------------------------------------
item_tb
| item_id | item_name |
-----------------------
user_tb
| user_id | user_name |
-----------------------
An item can belong to one or more user and viceversa, that's why I'm using the first table.
So, given the user_id = A and user_id = B how can I do a mysql query to select all the items the belong both to user A and user B?
note: I wrote a similar question yesterday but was about two tables not three.
SELECT i.*
FROM item_tb AS i
LEFT JOIN item_to_user AS iu
ON iu.item_id = i.item_id
LEFT JOIN user_tb AS u
ON iu.user_id = u.user_id
WHERE u.user_name IN ('A', 'B')
GROUP BY i.item_id
HAVING COUNT(i.item_id) > 1
By prequerying common items between A and B (via count(*) = 2) will pre-limit the final list of items possible to get details from. Then joining that to the items table as SECOND table in the query should help performance. Especially if A&B have 50 common items, but your items table consists of 1000's of items.
select straight_join
i.item_id,
i.item_name
from
( select iu.item_id
from item_to_user iu
join user_tb u
on iu.user_id = u.user_id
and u.user_name in ( 'A', 'B' )
group by 1
having count(*) = 2 ) Matches,
item_tb i
where
Matches.item_id = i.item_id
If a user can't have repeated items then this simple one will work:
select item_id
from item_to_user
where user_id in ('A', 'B')
group by item_id
having count(*) > 1