MySQL count rows in a joint table - mysql

I would like to count the number of rows or in this case the number of products which are in the same category and return zero if there are no products in the category.
The tables in my query looking like this:
category category_lang media
------------ ------------- ---------
category_id | published category_id | name | alias media_id | category_id
----------------------- -------------------------- ----------------------
1 | 1 1 | One | one 1 | 1
2 | 1 2 | Two | two 2 | 2
3 | 1 3 | Three| three 3 | 3
media_lang product_category
------------------------------- ----------------
media_id | url | file_name product_id | category_id
------------------------------- ------------------------
1 | /images/ | file1.jpg 1 | 1
2 | /images/ | file2.jpg 2 | 1
3 | /images/ | file3.jpg 3 | 2
and I would like a result like this:
category_id | category_name | alias | media_id | url | file_name | count
1 | One | one | 1 | /images/ | file1.jpg | 2
2 | Two | two | 2 | /images/ | file2.jpg | 1
3 | Three | three | 3 | /images/ | file3.jpg | 0
My query currently looks like this
SELECT
c.`category_id`,
ca.`name`,
ca.`alias`,
m.`media_id`,
ma.`url`,
ma.`file_name`,
COUNT(p.`product_id`) AS `count`
FROM `category` c
LEFT JOIN `category_lang` ca ON (c.`category_id` = ca.`category_id`)
LEFT JOIN `media` m ON (c.`category_id` = m.`category_id`)
LEFT JOIN `media_lang` ma ON (m.`media_id` = ma.`media_id`)
LEFT JOIN `product_category` p ON (c.`category_id` = p.`category_id`)
WHERE c.`published` = 1
ORDER BY ca.`name`
my logic is obviously wrong because this query will return this:
category_id | category_name | alias | media_id | url | file_name | count
1 | One | one | 1 | /images/ | file1.jpg | 3
How can I achieve the desired result?

I think the most painless (and sane) way to approach this would be to just aggregate the product counts for each category from the product_category table in a separate subquery, and then just join this to what you already have:
SELECT
c.category_id,
ca.name,
ca.alias,
m.media_id,
ma.url,
ma.file_name,
COALESCE(t.cat_count, 0) AS cat_count
FROM category c
LEFT JOIN category_lang ca
ON c.category_id = ca.category_id
LEFT JOIN media m
ON c.category_id = m.category_id
LEFT JOIN media_lang ma
ON m.media_id = ma.media_id
LEFT JOIN
(
SELECT category_id, COUNT(*) AS cat_count
FROM product_category
GROUP BY category_id
) t
ON c.category_id = t.category_id
WHERE c.published = 1
ORDER BY ca.name
Note here that your product_category table does not have any category entries with no products. This is not a problem, because in the LEFT JOIN we can simply treat a NULL count as being zero. A NULL value would occur if a given category did not match to anything in the subquery.

Seems to me that it is where you are doing the count and that you doing have the criteria for doing the count. Adding an extra condition where product category id equals category id in your category table should fix the incorrect count.
SELECT
c.`category_id`,
ca.`name`,
ca.`alias`,
m.`media_id`,
ma.`url`,
ma.`file_name`,
COUNT(p.`product_id`) AS `count`
FROM `category` c
LEFT JOIN `category_lang` ca ON (c.`category_id` = ca.`category_id`)
LEFT JOIN `media` m ON (c.`category_id` = m.`category_id`)
LEFT JOIN `media_lang` ma ON (m.`media_id` = ma.`media_id`)
LEFT JOIN `product_category` p ON (c.`category_id` = p.`category_id`)
WHERE (c.`published` = 1) and (c.`category_id` = p.`category_id`)
ORDER BY ca.`name`

Related

SQL - Display value in column when left join fails

Let's say I want to join on 2 tables, one is filled like this.
| Category |
|-----------|
| id | name |
|----|------|
| 1 | Foo |
| 2 | Bar |
| 3 | Baz |
The other like this:
| Page |
|-----------|
| id | cat |
|----|------|
| 1 | 0 |
| 2 | 1 |
| 3 | 3 |
As you can see cat 0 in the Page table is not present in the Category table. Our system is unfortunately like this and I can't add the category with id 0 to the Category table, due to other code.
Now comes the one million dollar question: Is it possible to join category.id on page.cat and set an if statement when page.cat equals 0 to show the category name as Default?
If only 0 is the one missing do a left join and use COALESCE to decode the nulls.
SELECT Page.*, COALESCE(name , 'Default')
FROM Page
LEFT JOIN Category
ON Page.cat = Category.id;
or
SELECT P.*, IFNULL(C.name , 'Default') as Name
FROM Page P
LEFT JOIN Category C ON P.cat= C.id
Try this out:
SELECT
p.id,
p.cat,
case when p.cat = 0 then "DEFAULT" ELSE c.name END AS cat_name
FROM
cat c RIGHT JOIN page p
ON c.id = p.cat
ORDER BY p.id

Need MySQL query from three tables

I have three tables:
Items:
Id |Name | Price | Cat_Id
---- |-------|----------------
1 | Item1 | 50.00 | 1
2 | Item2 | 25.20 | 5
Category:
Id |Name |
---- |------|
1 | Cat1 |
2 | Cat2 |
Discount:
Id |Item_Id| Client_Id|Discount
---- |-------|----------------
1 | 1 | 1 | 10
2 | 1 | 2 | 15
3 | 2 | 2 | 6
I am trying to get all items with proper discount, which is different for every customer. In this example i have client with Client_Id 1, which have discount for Item1/10/ and he doesn`t have discount on Item2.
The result should look like this:
Id |Name | Price | Cat | Discount
---- |-------|----------------|----------
1 | Item1 | 50.00 | Cat1| 10
2 | Item2 | 25.20 | Cat5| 0
My question is what is the way to build the query. I join first two tables and need to filter the third, but should I use temp table or do query in query?
It's Simple SQL Query.
Select Id, Name, Price, Cat, Discount From Items
left join discount on Items.id=discount.Item_Id
left join category on Items.cat_id=id
Output Like:
Id |Name | Price | Cat | Discount
---- |-------|----------------|----------
1 | Item1 | 50.00 | Cat1| 10
2 | Item2 | 25.20 | Cat5| 0
Try this way:
select di.id,di.Client_Id,it.Name,it.Price,ca.Cat,ifnull(di.Discount,0)
from Discount di
right join Items it on di.Item_id=it.Id
left join Category ca on it.Cat_Id=ca.Id
order by di_Id,di.Client_id,It.Name
You get all rows from discount and then get all items and look for the category. If an item has no discount(null) you get a 0
To get the result you want,
following is the query...
select
i.id item_id,
d.id discount_id,
i.name,
i.price,
c.name cat_name,
d.discount
from items i
left join discount d on i.id = d.item_id
left join category c on i.cat_id = c.id
where d.client_id = 1 or d.client_id is null;
/*where condition added after update as OP required*/
UPDATE
As per OP's comment
select
i.id item_id,d.id discount_id,
i.name,i.price,c.name cat_name,d.discount
from discount d
left join items i on i.id = d.item_id
left join category c on i.cat_id = c.id
where d.client_id = 2;

MySQL - How to make more than 1 where clause on same column?

Well i'm trying to make a where clause on same column many times i.e:
product
----------------------------------------
| product_id | product_name | group_id |
----------------------------------------
group
-------------------------
| group_id | group_name |
-------------------------
group_val
-------------------------
| group_id | product_id |
-------------------------
select b.product_name
from group_val a
inner join product b using(product_id)
inner join group c using(group_id)
where a.group_id in(10,15,88)
group by b.product_id;
it's same as or ... or but i need to do something like: it only will return product that are on group 10 and group 15 and group 88: I mean that a product need to be in these 3 groups.
i.e:
group_val
-------------------------
| group_id | product_id |
-------------------------
| 10 | 1 |
-------------------------
| 15 | 1 |
-------------------------
| 88 | 1 |
-------------------------
| 15 | 2 |
-------------------------
| 10 | 2 |
-------------------------
In this case it will only return product 1
How can I do it?
Just working with your example query, and assuming that you explicity know, programmatically or otherwise, which group_ids you're looking to match and how many there are:
select `product_name`, count(gv.`product_id`) as cnt
from `product` p
left join `group_val` gv
on p.`product_id` = gv.`product_id`
where gv.`group_id` in (10, 15, 88)
group by p.`product_id`
having cnt = 3;
will select all the product_ids in group_val that is in all of those groups.
You don't need the field group_id in table product
SELECT p.product_name
FROM product AS p
INNER JOIN group_val AS g1 ON p.product_id = g1.product_id
INNER JOIN group_val AS g2 ON p.product_id = g2.product_id
INNER JOIN group_val AS g3 ON p.product_id = g3.product_id
WHERE g1.group_id = 10 AND g2.group_id = 15 AND g3.group_id=88;
Try it in SQL Fiddle

Concatenating rows in relation to a JOIN

Suppose I have a cooking show:
cookingepisodes
id | date
---------------
1 | A
2 | B
3 | C
4 | D
…
This show reviews products in these categories (left) and are linked by the table to the right:
tests testitems
id | name id | episodeid | testid | name
------------ ------------------------------------
1 | cutlery 1 | 1 | 1 | Forks
2 | spices 2 | 2 | 1 | Knives
3 | 4 | 1 | Spoons
4 | 4 | 2 | Oregano
My desired output is this:
showid | testid | testname
4 | 1,2 | cutlery, spices
3 | NULL | NULL
2 | 1 | cutlery
1 | 1 | cutlery
I've tried using this query, and it works as long as I don't need to concatenate the results (when there are two tests on the same episode). Then the join will create multiple rows based on the number of
SELECT DISTINCT e.*, i.testid, t.name AS testname
FROM cookingepisodes AS e
LEFT OUTER JOIN testitems AS i ON i.episodeid = e.id
LEFT OUTER JOIN tests AS t ON i.testid = t.id
ORDER BY e.date DESC
I've also tried something like this, but I can't get it to work because of the outer block reference (e.id):
JOIN (
SELECT GROUP_CONCAT(DISTINCT testid)
FROM testitems
WHERE testitems.episodeid = e.id
) AS i
Any tips on how I can solve this without restructuring the database?
Try this one -
SELECT
ce.id showid,
GROUP_CONCAT(te.testid) testid,
GROUP_CONCAT(t.name) testname
FROM cookingepisodes ce
LEFT JOIN testitems te
ON te.episodeid = ce.id
LEFT JOIN tests t
ON t.id = te.testid
GROUP BY
ce.id DESC;

mysql left join problem with SUM and WHERE clause

I have 2 tables in my database: item and category.
Items can be active, or inactive, and have a categoryID that relates to the id of a record in the category table.
i want to perform a query to show all the categories, with the total cost of active items for the category
So my goal is to return something looking like this:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 12 |
| 2 | cat 2 | 0 |
| 3 | cat 3 | 45 |
+--------+------------+---------------+
My first query:
SELECT a.*,
SUM(b.cost) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
GROUP BY a.category_name
works ok, but it returns NULL items instead of 0, and uses all items regardless of active/inactive:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 44 |
| 2 | cat 2 | NULL |
| 3 | cat 3 | 87 |
+--------+------------+---------------+
my second query adresses the NULL values:
SELECT a.*,
SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
GROUP BY a.category_name
and turns out like so:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 44 |
| 2 | cat 2 | NULL |
| 3 | cat 3 | 87 |
+--------+------------+---------------+
So in my tiny useless brain i try the following query, adding a WHERE clause on table b where active has to = 1 (true)
SELECT a.*,
SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
WHERE b.active = 1
GROUP BY a.category_name
and i get the following:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 12 |
| 3 | cat 3 | 45 |
+--------+------------+---------------+
so as you can se, i would like to return the entire range of categories, even when the right table returns no matching results... Any takes for a million imaginary cool points?
Use:
SELECT c.id,
c.cat_name,
COALESCE(SUM(i.cost), 0) AS total_cost
FROM CATEGORIES c
LEFT JOIN ITEMS i ON i.category_id = c.category_id
AND i.active = 1
GROUP BY c.id, c.cat_name
Try this:
SELECT a.*,
SUM(Case B.Active When 1 Then b.cost else 0 End) AS total_cost
FROM categories a
LEFT JOIN items b
ON b.category_id = a.id
GROUP BY a.category_name
or this:
SELECT a.*, SUM(b.cost) AS total_cost
FROM categories a
LEFT JOIN items b
ON b.category_id = a.id
And B.Active = 1
GROUP BY a.category_name