How to make COUNT not count(fieldname) NULL values? - mysql

I'm setting up requests for e-commerce backend.
I have 3 tables: product (with id as index), category (with id as index) and product_category, which binds first 2 tables, since one product can be in several categories and there can be several products in one category.
The request is to get a list of all categories containing names of categories and number of products in this category including zero values (when category contains no products). Last 2 columns of the results shown.
Unfortunatelly, COUNT(fieldname) gives me 1 instead of 0.
Here's my SQL request:
SELECT product_category.id_category AS pr_cat_cat_id,
id_product AS pr_cat_pr_id,
product.name AS productname,
categories.id,
categories.name,
COUNT ('pr_cat_cat_id') AS quantity
FROM product
LEFT JOIN product_category ON product_category.id_product = product.id
RIGHT JOIN (SELECT * FROM category) AS categories
ON categories.id = product_category.id_category
GROUP BY name
ORDER BY id ASC
and get this result:
pr_cat_cat_id pr_cat_pr_id productname id name quantity
1 1 Product "Name1" 1 Category 1 2
2 3 Product "Name 3" 2 Category 2 2
NULL NULL NULL 3 Category 3 1
NULL NULL NULL 4 Category 4 1
NULL NULL NULL 5 Category 5 1
NULL NULL NULL 6 Category 6 1
I do expect quantity to be zero on categories without products.

You are counting a constant string value, which is never NULL. Use quotes correctly. You don't need them here:
COUNT(product_category.id_category) AS quantity
You cannot use an alias for the COUNT(). You need to refer to the original column.
Note that your query is malformed. The only things in the select should be name and the aggregation functions.

Column aliases from the same select list can't be referenced. Instead use its original column name:
COUNT(product_category.id_category)
Note: Single quotes are for string literals, and those are never null.

Related

With SQL how to select rows that the joined table matches 2 values

If we have a table, orders:
Order ID
Order Table
1
100
1
50
And we have a table, OrderProducts:
OrderID
Product ID
ProductName
ProductType
1
1
ProductOne
Small
1
2
ProductTwo
Big
1
3
ProductThree
Small
2
4
ProductFour
Big
2
5
ProductFive
Big
How do I use SQL to return only the Orders that contain products of 2 specific types. In the scenario above I want to return only products with ProductType = 'Small' and 'Big' but ignore orders like "2" which only have products of two 'Big' on.
Results:
Order ID
1
I have tried creating a view to help, inner joins with multiple clauses but I am struggling. I am using MySQL for this and think my logic is simply "out".
Any advice is appreciated.
Aggregation is one method:
select order_id
from orderproducts op
where ProductType in ('Small', 'Big')
group by order_id
having count(distinct ProductType) = 2;

Joining multiple columns into one with union, exclude results with same id

I want to join columns from multiple tables to one column, in my case column 'battery_value' and 'technical_value' into column 'value'. I want to fetch data for only given category_ids, but because of UNION, I get data from other tables as well.
I have 4 tables:
Table: car
car_id model_name
1 e6
Table: battery
battery_category_id car_id battery_value
1 1 125 kW
Table: technical_data
technical_category_id car_id technical_value
1 1 5
3 1 2008
Table: categories
category_id category_name category_type
1 engine power battery
1 seats technical
3 release year technical
From searching, people are suggesting that I use union to join these columns. My query now looks like this:
SELECT CARS.car_id
category_id,
CATEGORIES.category_name,
value,
FROM CARS
left join (SELECT BATTERY.battery_category_id AS category_id,
BATTERY.car_id AS car_id,
BATTERY.value AS value
FROM BATTERY
WHERE `BATTERY`.`battery_category_id` IN (1)
UNION
SELECT TECHNICAL_DATA.technical_category_id AS category_id,
TECHNICAL_DATA.car_id AS car_id,
TECHNICAL_DATA.value AS value
FROM TECHNICAL_DATA
WHERE `TECHNICAL_DATA`.`technical_category_id` IN (3))
tt
ON CARS.car_id = tt.car_id
left join CATEGORIES
ON category_id = CATEGORIES.id
So the result I want is this, because I only want to get the data where category_id 1 is in battery table:
car_id category_id category_name technical_value
1 1 engine power 125 kW
1 3 release year 2008
but with the query above I get this, category_id 1 from technical table is included which is not something I want:
car_id category_id category_name value
1 1 engine power 125 kW
1 1 seats 125 kW
1 3 release year 2008
How can get exclude the 'seats' row?
For the results you want, I don't see why the cars table is needed. Then, you seem to need an additional key for the join to categories based on which table it is referring to.
So, I suggest:
SELECT tt.*, c.category_name
FROM ((SELECT b.battery_category_id AS category_id,
b.car_id AS car_id, b.value AS value,
'battery' as which
FROM BATTERY b
WHERE b.battery_category_id IN (1)
) UNION ALL
(SELECT td.technical_category_id AS category_id,
td.car_id AS car_id, td.value AS value,
'technical' as which
FROM TECHNICAL_DATA td
WHERE td.technical_category_id IN (3)
)
) tt LEFT JOIN
CATEGORIES c
ON c.id = tt.category_id AND
c.category_type = tt.which;
That said, you seem to have a problem with your data model, if the join to categories requires "hidden" data such as the type. However, that is outside the scope of the question.

MySQL related items query

I have related products table like this:
product_id | related_product_id
1 | 2
1 | 3
1 | 4
2 | 1
3 | 1
4 | 1
But instead I would like to insert new related product ids so they all match. I.E. If product 1 has 2,3,4 I wan't that products 2,3,4 also have the same related ids which are missing.
Not sure how it's called but is this possible? Many thanks.
You can use a SELECT query as the source of data in an INSERT
INSERT INTO related_products (product_id, related_product_id)
SELECT r1.product_id, r2.related_product_id
FROM related_products AS r1
CROSS JOIN related_products AS r2
WHERE r1.product_id != 1
AND r2.product_id = 1
This join will get all of product 1's related products and combine them with all the other product IDs.
You can give this query a try (untested, make a backup first!):
insert into related_products (product_id,related_product_id) (select related_product_id, product_id from related products);
I would suggest to user bidirectional condition to get the inter related products. For example if you apply condition on single column product_id, you will not get visa-versa result. But, if you check that condition on both column, you will get the result.
For example:
select related_product_id, product_id from products where related_product_id=1 OR product_id=1
This will give your related product id in either related_product_id or product_id.
Same you can get it for product 2 i.e.
select related_product_id, product_id from products where related_product_id=2 OR product_id=2
This will give all your related product id in either related_product_id or product_id.

Return all products from left table and results from rating table whether a given user rated them or not

I have 332 products in a selection table and would like to return exactly 332 results. Each product has been rated or not by a given user in the matching table. These are the results from two tables using the following query
SELECT a.product_id,a.brand,b.user_id,b.rating,b.comment
FROM selection a
LEFT JOIN matching b ON a.product_id = b.product_id
product_id brand user_id rating comment
1 A & P 30 4.5 NULL
1 A & P 30 4.5 NULL
1 A & P 52 1 NULL
2 A & W 1 3 good flavor
2 A & W 24 5 NULL
I want to return all the products with a rating and comment where user_id = 1 and then the remaining products where user 1 did not rate or comment on the product. I have tried
WHERE user_id = 1 OR
user_id <> 1 AND rating ID NULL and comment IS NULL
This will just return the same as WHERE user_id = 1. I have also tried GROUP BY product_id This returns the correct number of results but not all of the user's ratings or comments were controlled by this alone. Is there a way to define the user that it will select when returning the grouped results? Any other suggestions? Thanks!
If you meant for the WHERE statement to include two separate conditions you need to surround the second statement in parenthesis; otherwise SQL would treat each operator as its own condition:
WHERE user_id = 1 OR
(user_id <> 1 AND rating ID NULL and comment IS NULL)

Apply filter on joined tables with MySQL

I store some data in MySQL and want to filter out rows that match a given criteria.
Sounds easy, but it is not since there are some join criteria involved.
I do have the following tables:
items : id, ...
genres: id, name:varchar, item_id
Each item has multiple genres.
The query should filter out items if at least one genre does match a given genre name (or a set of names).
For example:
Item with id 1 has 3 genres
- genre name = 'foo'
- genre name = 'bar'
- genre name = 'baz'
Item 1 may not be part of the result set if the given genre name is 'bar', ['bar', 'baz', 'xyz'], etc.
I tried to left join the genres on the items and applied a WHERE statement with "genres.name NOT IN (?)". ? is the given set of genre names.
This (of course) only works for items with exactly one genre. The same could be achieved by multiple WHERE conditions: WHERE name <> 'a' AND name <> 'b' ...
Any ideas how to get this query done properly?
Thanks in advance!
You can do something like this (see sqlfiddle):
select i.name as item, g.name as genre
from items as i
left join genres as g
on i.id = g.item_id
where i.id not in
(select distinct g2.item_id from genres as g2
where FIND_IN_SET(g2.name,'foo,bar'));
And this way it works if you want to check against multiple genre names.
You can use a correlated subquery.
Example:
SELECT id
FROM items i
WHERE NOT EXISTS (SELECT 1
FROM genres g
WHERE g.`name` = 'bar'
AND i.id = g.item_id);
A correlated subquery differs from other subqueries in that it references the outer query.
Correlated subqueries are run for each row projected/selected by the outer query.
Well hopefully I understand what you are asking for and I think MySQL and T-SQL will be similar in this respect. These are the sample tables I assumed you were using from your example.
Table1
ID NAME
1 Item 1
2 Item 2
3 Item 3
4 Item 4
5 Item 5
6 Item 6
Table2
ID NAME ITEM_ID
1 Genre 1 1
2 Genre 2 1
3 Genre 3 1
4 Genre 1 2
5 Genre 2 2
6 Genre 3 2
7 Genre 1 3
8 Genre 2 3
9 Genre 3 3
10 Genre 1 4
Here is the SQL to filter out items if they match one of your criteria.
SELECT *
FROM table1 a, table2 b
WHERE a.id = b.item_id
AND b.name NOT IN ('Genre 1', 'Genre 2')
Results returned from above query on sample tables.
ID NAME ID_1 NAME_1 ITEM_ID
1 Item 1 3 Genre 3 1
2 Item 2 6 Genre 3 2
3 Item 3 9 Genre 3 3
You could also filter out against the genre_id (table2.id) instead. If you plan to use a sub-query instead of hard coded values then you should switch the "NOT IN" to a "NOT EXISTS" and re-write the "AND" portion accordingly as "NOT IN" does not like null values.
This query also assumes that each "Item" has at least 1 "Genre." I am sure you can play with it if you want all "Items" excluding the ones that match your criteria.
select * from items i where not exists
(select '' from genres gen where gen.item_id = i.id and gen.name in ('foo','pop') )
A shorter ans simpler query.
The inner query checks genre name in the keywords list , if the no row is returned then the current row of outer query is selected.
Verify the output
at here.
Thanks to Kyra
,i borrowed his schema from sql fiddle.