MySQL How do I count the number of occurences of each ID? - mysql

It may be difficult to explain what I am after, apologies if the question is vague.
I have a table which associates products with keywords using IDs
So I may have product IDs, 2,3,4,5 associated with Keyword id 14
and product IDs 3,6,9 associated with Keyword id 15
My question is How do I count and store the total for those IDs associated with Keyword 14 and for those IDs associated with Keyword 15 and so on (New Keywords added all the time)?
MY SQL so far:
select products_keyword_categories.key_cat_name
from products_keyword_to_product
inner join products_keyword_categories
on products_keyword_categories.key_cat_id = products_keyword_to_product.key_cat_id
group by products_keyword_categories.key_cat_name
Many thanks in advance for any advice. Also, if there is any terminology that will aid me in further research via a Google search that would also be most welcome.
Edit to add: In the example above the table containing the associations is products_keyword_to_product - I inner join the other table to return the Keyword name.
Edit to add (2): Sorry I was afraid my question would be vague.
If I wanted to just count all the products using keyword ID 14 I would use COUNT() AS - As mentioned in the answers but I also need to count the number of products using Keyword ID 15 and Keyword ID 16 etc. - Hope that makes more sense.

select key_cat_name ,count(*)
from products_keyword_categories pkc
inner join products_keyword_to_product ptk on pkc.id=ptk.key_id
group by id;

select cat.key_cat_name, count(*) from
products_keyword_categories cat inner join products_keyword_to_product prod
on prod.key_cat_id=cat.key_cat_id
group by cat.key_cat_name
Edit:
select cat.key_cat_name, prod_assoc.product_id, count(*) from
products_keyword_categories cat inner join products_keyword_to_product prod_assoc
on prod_assoc.key_cat_id=cat.key_cat_id
group by cat.key_cat_name,prod_assoc.product_id
Assuming your tables structure is like this:
products_keyword_categories
key_cat_id key_cat_name
1 Electronics
2 Toys
3 Software
products_keyword_to_product
key_cat_id product_id
1 1
2 1
3 2
1 2
products
product_id name
1 Product A
2 Robot
Edit 2:
Try this
SELECT key_cat_name, product_id, COUNT(*)
FROM
(select cat.key_cat_name, prod_assoc.product_id from
products_keyword_categories cat inner join products_keyword_to_product prod_assoc
on prod_assoc.key_cat_id=cat.key_cat_id) as tbl
GROUP BY key_cat_name, product_id
Edit 3:
The query above is made of 2 parts:
The inner part:
(select cat.key_cat_name, prod_assoc.product_id from
products_keyword_categories cat inner join products_keyword_to_product prod_assoc
on prod_assoc.key_cat_id=cat.key_cat_id)
Which gives 1 row per combination of product_id and key_cat_name.
The outer part:
SELECT key_cat_name, product_id, COUNT(*)
FROM (...) as tbl
GROUP BY key_cat_name, product_id
Which operates on the results of the inner part (as tbl), counting how many times a combination of key_cat_name and product_id appears on the inner part.
Check this: Subqueries in MySQL, Part 1

You are almost there, you just need to add the following:
select count(products_keyword_to_product.id), products_keyword_categories.key_cat_name
...
the rest is correct

Updated Answer:
SELECT COUNT(*), reference_field FROM table WHERE...
HAVING field=value
GROUP BY field
For aggregate conditions you must use HAVING

Related

SQL JOIN - comments with reactions (2 tables)

I need to make one SQL command.
From table with comments i'll get comment id, then
with this ID I need to get count of reactions with the same comment ID and user's names.
So for example I have this 2 tables:
Comments:
ID
Comm_text
1
Example text
2
Another example
and Reactions:
ID
comm_id
usr
etc..
1
1
Peter
another
2
1
John
collon
3
1
Dog
cuz
4
2
Cat
why not
I need to get this:
ID
Comm_text
Reactions_Count
Users
1
Example text
3
Peter, John, Dog
2
Another example
1
Cat
I tried this:
SELECT k.id, k.comm, COUNT(r.id) as reactions, r.usr
FROM `comms` k
INNER JOIN `reactions` r ON r.id=k.id
It's just one row with one comment and count of all rows in reaction table.
Thanks.
Try this query that makes the same output:
select comments.id as ID , comments.Comm_text as Comm_text ,
(select count(id) from Reactions where comm_id = comments.id) as Reactions_Count ,
(select coalesce(GROUP_CONCAT(usr ORDER BY usr DESC) , '') from Reactions WHERE comm_id = comments.id) as Users
from comments group by comments.id
You should use group by to group the comments and have just one row then use query to count and gather the data, based on each row of the group.
The GROUP_CONCAT attach the output with , and the coalesce set the output to a given string if the output was empty.
Read more about:
GROUP BY
GROUP_CONCAT
COALESCE
subquery
According to the names that u set in the example, this will work. Just fix the table names for your database structure.
SELECT `Comments`.`ID`, `Comments`.`Comm_text`, count(`Reactions`.`comm_id`) as react, `Reactions`.`usr`
FROM `Comments`
INNER JOIN `Reactions`
ON `Comments`.`ID`=`Reactions`.`comm_id`
GROUP BY `Reactions`.`comm_id`

SQL query involving comparison of sets

Background
Products can be sold as bundles. Following tables are present: products, bundles, bundles_products, orders, orders_products.
An order would be said to "contain" a bundle if it contains all the bundle's products.
Problem
How would one go about counting orders for bundles?
Example
products table
id name
1 broom
2 mug
3 spoon
4 candle
bundles table
id name
1 dining
2 witchcraft
bundles_products table
bundle_id product_id
1 2
1 3
2 1
2 4
orders_products table
order_id product_id
1000 1
1000 3
1001 1
1001 2
1001 3
The query would return the following table:
bundle orders
dining 1
witchcraft 0
Notes
The example intentionally misses the orders table as it is not relevant what it contains.
Of course, this could be approached imperatively, by writing some code and gathering the data, but I was hoping there is a declarative, SQL way of querying for this kind of things?
One idea I had was to use a GROUP_CONCAT to concatenate all the products of a bundle and somehow compare that with products of each order. Still, a long way from clear.
One way is to use two Derived Tables (subqueries). In first subquery, we will fetch the total number of unique products for every bundle. In the second subquery, we will fetch the total products in an order, for a combination of order and bundle.
We will LEFT JOIN them on bundle_id as well as matching the total count of products per bundle in them. Eventually, we will do a grouping on bundle, and count the number of orders matching successfully.
SELECT dt1.id AS bundle_id,
dt1.name AS bundle,
Count(dt2.order_id) AS orders
FROM (SELECT b.id,
b.name,
Count(DISTINCT bp.product_id) AS total_bundle_products
FROM bundles AS b
JOIN bundles_products AS bp
ON bp.bundle_id = b.id
GROUP BY b.id,
b.name) AS dt1
LEFT JOIN (SELECT op.order_id,
bp.bundle_id,
Count(DISTINCT op.product_id) AS order_bundle_products
FROM orders_products AS op
JOIN bundles_products AS bp
ON bp.product_id = op.product_id
GROUP BY bp.bundle_id,
op.order_id) AS dt2
ON dt2.bundle_id = dt1.id
AND dt2.order_bundle_products = dt1.total_bundle_products
GROUP BY dt1.id,
dt1.name
SQL Fiddle DEMO
Here's the brief example, which lacks some parts, I omitted because I don't know precise database structure. Logic is such:
Temp table is generated, which consists of 3 rows - order, count of
products related to bundle, count of products in bundle
Then we select only orders from this table in which we have those last two
variables equal
select count(order_id) from orders
left join(
select count(*) from bundles_products as bundle_amount,
sum(case when orders_products in (
select names from bundles_products where bundle_id='1') then 1 else 0) as order_total,
orders.order_id
left join product on bundle_products.product_id = products.product_id
left join orders on products.product_id = orders_products.product_id
where bundle_products.bundle_id ='1'
) as my_table
on orders.order_name = my_table.orders
where my_table.bundle_amount = my_table.order_total
Edit: I posted this as a response to previous version of the question, without detailed explanation.
Edit2: fixed query a bit. It can be starting point. Logic is still the same, you can get amount of orders for each bundle_id using it

SQL - Counting how many associated records another table has

As I'm SQL beginner, I can't describe a problem in a simple way, so let me show you an example:
3 Tables:
PRODUCT
id
group_id
person_id
GROUP
id
name
PERSON
id
group_id
As you see, GROUP can have multiple PERSONs and PRODUCT can be connected with GROUP and PERSON.
From this point, I would like to count number of PERSONs having a PRODUCT within a GROUP
I don't really understand the background of IN or using another SELECT within FROM, so if that's the point, then I'm happy that I was one step before it lol.
SELECT
group.name as GROUP_name,
COUNT(DISTINCT person_id) AS PERSON_having_min_one_PRODUCT
FROM products
LEFT JOIN groups ON groups.id = products.group_id
LEFT JOIN persons ON persons.id = products.person_id;
With this data:
GROUP
ExampleGroupName1 has 3 PERSONs, but 2 of them has >0 PRODUCTS
ExampleGroupName2 has 3 PERSONs and all of them has >0 PRODUCTS
ExampleGroupName3 has 2 PERSONs, but none of them has the PRODUCT
ExampleGroupName4 has 2 PERSONs, but only 1 has >0 PRODUCT
I would like to have an output like this:
GROUP_name | PERSON_having_min_one_PRODUCT
ExampleGroupName1 | 2
ExampleGroupName2 | 3
ExampleGroupName4 | 1
I would like to count number of PERSONs having a PRODUCT within a GROUP
Note: I will assume the table product does not have the column group_id, since it is redundant and can lead to a lot of errors.
The following query will show you the result you want by joining the tables person and product:
select
count(distinct x.id)
from person x
join product p on p.person_id = x.id
where x.group_id = 123 -- choosing a specific group
and p.id = 456 -- choosing a specific product
This would rather be simple like below meaning all the groups with some group_id with count(persons) and those count who has some product via id used in having clause
Select group_id,
count( distinct id ) AS "PERSON_WITH_PRODUCT"
from
person group by group_id having id
in (Select id from product);

How to order by a "calculated" (not MySQL function) value?

there are students and their marks:
Student:
ID NAME
1 a
2 b
3 c
4 d
Mark:
ID STUDENT_ID MARK
1 1 4
1 1 2
1 3 4
I would like to get an ordered list about their average marks, to get:
a, (4+2)/2
b, -,
c, 4/1
d, -
I must not calculate it while SELECT-ing, not even store it in a column in Students table. The interesting part is when I would want to order and limit (paging) the result. To have a correct ordering, I must know all averages - so I wont use SELECT query without LIMIT. But how to? Ideally, ordering by ID or NAME is easy.
You can order by an expression, including a subquery:
order by (select avg(mark) from marks m where m.student_id = s.id)
I'm not sure I would want to do this as part of paging, unless the underlying data is pretty small.
You can get users with their average marks with this query:
SELECT student.*, AVG(mark.mark) as average_mark
FROM users
LEFT OUTER JOIN mark m on m.student_id= users.id
GROUP BY users.id
ORDER BY average_mark DESC

Join two MySQL Tables and get result from categories

SELECT art.*,arg. FROM rd_articles AS art
LEFT JOIN rd_argument AS arg ON art.cat=arg.id WHERE art.enabled=1 ORDER BY art.id DESC
LIMIT 10
This is simple join query
Article table structure is
ID cat Description Date
1 1 Abc 08-01-2014
2 1 Aaa 10-01-2014
3 2 Abcv 11-01-2014
4 3 Aaa 12-01-2014
5 3 Aaa 14-01-2014
Arguments table is
ID Name
1 A
2 B
3 C
I want pick last updated(Date) one item from each cat.
How ?
This assumes that the enabled column is in rd_articles:
SELECT art.*, arg.*
FROM (
SELECT * FROM rd_articles
INNER JOIN (
SELECT cat, MAX(date) AS maxdate
FROM rd_articles
WHERE enabled = 1
GROUP BY cat
) md ON rd_articles.cat = md.cat AND rd_articles.date = md.maxdate
) art
LEFT JOIN rd_argument AS arg ON art.cat = arg.id
The innermost query gets the maximum date for each category, then joins it to the rd_articles table to get only those rd_articles rows that have the latest date for each article. That becomes the cat alias, which is then left-joined to the arguments table just like in your original query. You can add the LIMIT 10 at the end if needed; I wasn't sure what to do with that.
Note that if there's a tie for a category's latest date, you'll get more than one row for each category. If a tie could happen you'll need to break the tie somehow, for example by using the description or the ID. Let me know if that's the case and I'll update my answer.
SELECT ART.*, ARG.*
FROM ARTICLE AS ART
INNER JOIN RD_AGRUEMENT AS ARG
ON ARG.ID = ART.ID
WHERE (ID, DATE) IN
(SELECT ID, MAX(DATE) FROM ARTICLE GROUP BY ID)