I have a table called products with 3 columns. One of the column is product_id. This column contains duplicate product ids. From another source I was getting some other product ids. I want to display the not existing product ids count . For example I am getting product ids 12,13,14,15,16 from one source. In my products table I am having 12,13,14. So now I want to display the count of not existing product ids . In this case that should be 2. To achieve that I tried with below query.
SELECT COUNT(DISTINCT product_id)
FROM products p1
WHERE NOT EXISTS
(SELECT COUNT(DISTINCT product_id)
FROM products p2
WHERE p2.product_id IN (12,13,14,15,16) AND p1.product_id = p2.product_id)
But I am getting empty result. Any help would be greatly appreciated.
If the list of products comes from an application, and you know the length of the list as well (in this case 5) you can find the number of non-existent products in the table like this:
SELECT 5 - COUNT(DISTINCT product_id) AS non_existent_products
FROM products
WHERE product_id IN (12, 13, 14, 15, 16)
Output
non_existent_products
2
If you don't know the length of the list, you can figure it out using e.g.
LENGTH('(12, 13, 14, 15, 16)') - LENGTH(REPLACE('(12, 13, 14, 15, 16)', ',', '')) + 1
Demo on dbfiddle
Update
If the other product_id values are coming from another query (e.g. SELECT product_id FROM othertable) you can find the count of products in that result that are not in the products table using a LEFT JOIN of the results from that query with the products table and taking the difference in COUNTs of product_id from the two. Something like this:
SELECT COUNT(DISTINCT q.product_id) - COUNT(DISTINCT p.product_id) AS non_existent_products
FROM (SELECT product_id FROM products2) q
LEFT JOIN products p ON p.product_id = q.product_id
Demo on dbfiddle
Let's assume, we have 2 tables having the following values:
INSERT INTO `products` (`product_id`)
VALUES (1),(2),(3),(4),(1),(3),(1),(2),(5)
;
INSERT INTO `other_products` (`product_id`)
VALUES (2),(4)
;
Then this query gives you the count of each id 1, 3 and 5 (not existing in other_products)
SELECT `p`.`product_id`, COUNT(`p`.`product_id`) `count`
FROM `products` `p`
LEFT JOIN `other_products` `o`
USING (`product_id`)
WHERE `o`.`product_id` IS NULL
GROUP BY `p`.`product_id`
;
while this one gives you the total distinct ids not existing in the other table
SELECT count(DISTINCT `p`.`product_id`)
FROM `products` `p`
LEFT JOIN `other_products` `o`
USING (`product_id`)
WHERE `o`.`product_id` IS NULL
;
See Live Demo
you can use something like this
select count(A.product_id) from (select distinct product_id from product where product_id not in (1,2)) AS A;
here you are selecting distinct product_id from table product where product id's are not equals to 1 and 2 then creating an alias for the result as A.
A acts as temporary table, so you need to use A.product_id to select.
Sorry for the bad english, but it i hope it meets requirement of question.
The problem you are having it's that your sub query always returns a value (some times returns 0) so when you do NOT EXISTS () it's allways FALSE, because you allways get an answer.
You should change NOT EXISTS by 0> like this
SELECT COUNT(DISTINCT product_id)
FROM products p1
WHERE 0>
(SELECT COUNT(DISTINCT product_id)
FROM products p2
WHERE p2.product_id IN (12,13,14,15,16) AND p1.product_id = p2.product_id)
Related
SELECT
category_id,
product_size,
category_name,
SUM(product_quantity) AS total_quantity
FROM tbl_categories_quantity -- (table-1)
INNER JOIN tbl_categories USING (category_id)
GROUP BY category_id,product_size
The Above Code is working in a single table, and I want to Add the below code (2nd table) that does not work
SELECT
category_id,
product_size,
SUM(product_sell) AS total_sell
FROM tbl_product_sell -- (table-2)
GROUP BY category_id,product_size;
From first subquery retrieves category and product size wise total quantity and second one retrieves total sales based on category and product size. Then combine this two subquery with LEFT JOIN because sometimes sale may not happen. COALESCE() is used for replacing NULL value to 0 (zero). If specific category or product size wise data are required then use WHERE clause in both the subquery. As category id is unique so MAX(category_name) is used otherwise category name must be placed in GROUP BY clause. Subtract total sale from total quantity for calculating available quantity.
-- MySQL
SELECT t.category_name category
, t.product_size
, t.product_quantity
, COALESCE(p.total_sell, 0) product_sell
, (t.product_quantity - COALESCE(p.total_sell, 0)) available_in_stock
FROM (SELECT tc.category_id
, tcq.product_size
, MAX(tc.category_name) category_name
, SUM(tcq.product_quantity) product_quantity
FROM tbl_categories tc
INNER JOIN tbl_categories_quantity tcq
ON tc.category_id = tcq.category_id
GROUP BY tc.category_id
, tcq.product_size) t
LEFT JOIN (SELECT category_id
, product_size
, SUM(product_sell) total_sell
FROM tbl_stock_sell
GROUP BY category_id
, product_size) p
ON t.category_id = p.category_id
AND t.product_size = p.product_size
Please check from url https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=b8c54aa656d9dc930fcb7a93d2bc0960
N.B.: Table name or column name may vary based on your DB.
I have three tables (SQLFiddle with tables created)
Orange text is what I need to get by comparing Products.name with Filters.filter.
I figured out that substring match can be done like this:
on Products.name LIKE CONCAT('%',Filters.filter,'%');
I only need the first filter match to be used. So "Mushroom soup" will match "Soup" and not "Mushroom".
What is best approach for this task?
A tested SQLFiddle link, if possible, please.
What I tried: (feel free to skip everything below)
Tried double join:
update Products
left join Filters on Products.name LIKE CONCAT('%',Filters.filter,'%')
join Categories on Filters.category_name = Categories.name
set Products.category_id = Categories.id, Products.filter = Filters.filter;
But this doesn't result in first match being used, because first join returned all filter matches (multiple lines per 1 product), and after second join categories are from last match.
For example: Mushroom soup got into "Mushroom-Ingredients" filter\category, instead of "Soup\Meals".
Also tried join + order by:
select Products.name, Filters.*
from Products
left join Filters on Products.name LIKE CONCAT('%',Filters.filter,'%')
group by Products.name;
This returns the first match per product as I needed, but I can't do a second left-join in the same query, neither can I get "group by" to work after "update" function instead of "select".
For when sqlfiddle collapses:
CREATE TABLE Products
(
`name` varchar(30),
`category_name` varchar (30),
`category_id` int
);
INSERT INTO Products (`name`)
VALUES ('Mushrooms'), ('Can of mushrooms'),
('Can of soup'), ('Mushroom soup'),
('Tomato soup'), ('Tomato');
CREATE TABLE Filters
(
`filter` varchar(30),
`category_name` varchar(30)
);
INSERT INTO Filters (`filter`, `category_name`)
VALUES ('Can of', 'Canned food'), ('Soup', 'Meals'),
('Mushroom', 'Ingredients'), ('Tomato', 'Ingredients');
CREATE TABLE Categories
(
`id` int,
`name` varchar(30)
);
INSERT INTO Categories (`id`, `name`)
VALUES (1, "Canned food"), (2, 'Ingredients'), (3, 'Meals');
You made the task quite easy with your sqlfiddle as well as your attempt to solve the problem with Select query.
It works the way you want I guess and all I need to do is to add one more left join with category table (IDK why you were unable to join Category as it's working properly).
So. I've edited your select query as follows:
select Products.name, Filters.*,Categories.id from Products
left join Filters
on Products.name LIKE CONCAT('%',Filters.filter,'%')
left join Categories
on Categories.name = Filters.category_name
GROUP BY Products.name;
You will get the results you want with this query.
Now, in order to update Products table with the result of this query, you can do following:
update Products
left join Filters
on Products.name LIKE CONCAT('%',Filters.filter,'%')
left join Categories
on Categories.name = Filters.category_name
set Products.category_name = Filters.category_name,
Products.category_id = Categories.id;
Click here for Working Demo
Hope it helps!
Table Fields:
shop_id , product_id
I want a list of all shops having specific products(should have at least 1 product)
results should be sorted on basis of shops having maximum number of specified products
I could write sql query for 1st part, but the list is not sorted according to the shops that match maximum number of products
SELECT
shop_id,
product_id
FROM
products_table
WHERE
product_id IN (1,2,3)
ORDER BY ???
Is there a optimal solution?
Join with a subquery that gets the counts for each shop, and order by that.
SELECT a.shop_id, a.product_id
FROM products_table AS a
JOIN (SELECT shop_id, COUNT(*) AS product_count
FROM products_table
WHERE product_id in (1, 2, 3)
GROUP BY shop_id) AS b
ON a.shop_id = b.shop_id
WHERE product_id IN (1, 2, 3)
ORDER BY b.product_count DESC
A query like this will avoid repeating the list of product_ids:
with sp as (
select shop_id, product_id
from products_table
where product_id IN (1,2,3)
)
select
shop_id, product_id,
(select count(*) from sp as sp2 where sp2.shop_id = sp.shop_id) as shop_count
from sp
order by shop_count desc
But now I see you're using MySQL so it won't work out for you although it can be expanded:
select
shop_id, product_id,
(
select count(*) from products_table as p2
where product_id in (1,2,3) and p2.shop_id = p.shop_id
) as shop_count
from products_table as p
where product_id in (1,2,3)
order by shop_count desc;
It's essentially the same query but the join is implied. I'm under the impression that MySQL doesn't always handle correlated queries very efficiently. I think the flavor of Barbar's answer is the one you'll have to use unless you create a temporary table mirroring "sp" above.
As a side note, I study languages and it's interesting to me that I chose to call my computed column "shop_count" while the other Barmar went with "product_count. I focused on shop as the center of my attention though we're actually counting up products. To me "shop_count" indicated "count per shop" while Barbar might describe his as "count of products". By no means am I arguing that one approach is more valid or natural. It's just fascinating to me to see the different perspective that people can take.
Please excuse the awkward title for this question.
I am trying to keep track of the prices of some products, ideally without having a load of entries with the same product_id and price (different dates). There are 160,000 products and the update is run every day.
I have the following tables:
products
product_id, price, date_added, date_updated
(id Primary Key)
price_index
product_id, price, date_updated
(product_id, price, date_updated primary unique)
I am doing the following query, however I cannot sort the select/join by the most recent date.
INSERT INTO price_index
(SELECT p.product_id,p.product_price,p.date_updated FROM products p
LEFT JOIN price_index pi ON (p.product_id = pi.product_id)
WHERE (p.product_price <> pi.product_price OR pi.product_price IS NULL))
If I replace the JOIN with
JOIN (SELECT * FROM price_index ORDER BY date_updated DESC) pi
It will do the correct sorting first, however this doesn't seem to use any indexed and keeps freezing my MySQL GUI tool.
Is there a better way to achieve what I am trying to do here?
UPDATE:
The update script updates the products table with the current price first. I then need to perform the check/update on the price_index table.
If I understand correctly, you want to insert a new row only when the most recent price has changed. I'm a little confused about the tables. Your query mentions three but your text only mentions two. I'll assume that product_prices and price_index are the same thing.
INSERT INTO product_prices(product_id, price, date_updated)
SELECT p.product_id, p.product_price, p.date_updated
FROM products p LEFT JOIN
price_index pi
ON p.product_id = pi.product_id AND
p.product_price = pi.product_price LEFT JOIN
(SELECT product_id, max(date_updated) as maxdu
FROM price_index
GROUP BY product_id
) pimax
ON pi.product_id = pimax.product_id and pi.date_updated = pimax.maxdu
WHERE pimax.product_id IS NULL;
This uses a left join to look for a match on both the product and the price and on the most recent
update date. If any of the three conditions do not match, then the insert takes place.
Given the following (very simplified) mysql table structure:
products
id
product_categories
id
product_id
status (integer)
product_tags
id
product_id
some_other_numeric_value
I am trying to find every product that has an association to a certain product_tag, and that a relation to at least one category whichs status-attribute is 1.
I tried the following query:
SELECT *
FROM `product` p
JOIN `product_categories` pc
ON p.`product_id` = pc.`product_id`
JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`
WHERE pt.`some_value` = 'some comparison value'
GROUP BY p.`product_id`
HAVING SUM( pc.`status` ) > 0
ORDER BY SUM( pt.`some_other_numeric_value` ) DESC
Now my problem is: The SUM(pt.some_other_numeric_value) returns unexpected values.
I realized that if the product in question has more then one relation to the product_categories table, then every relation to the product_tags table is counted as many timed as there are relations to the product_categories table!
For example: If product with id=1 has a relation to product_categories with ids = 2, 3 and 4, and a relation with the product_tags with ids 5 and 6 - then if I insert a GROUP_CONCAT(pt.id), then it does give 5,6,5,6,5,6 instead of the expected 5,6.
At first I suspected it was a problem with the join type (left join, right join, inner join, and so on), so I tried every join type that I know of, but to no avail. I also tried to include more id-fields into the GROUP BY clause, but this didnĀ“t solve the problem either.
Can somebody explain to me what is actually going wrong here?
You join a "main" (product) table to two tables (tags and categories) via 1:n relationships, so this is expected, you are creating a mini cartesian product. For those products that have both more than one associated tags and more than one associated categories, multiple rows are created in the result set. If you Group By, you have wrong results in aggregate functions.
One way to avoid this is to remove one of the two joins, which is a valid startegy if you don't need results from that table. Say you don't need anything in the SELECT list from the product_categories table. Then you can use a semi-join (the EXISTS subquery)to that table:
SELECT p.*,
SUM( pt.`some_other_numeric_value` )
FROM `product` p
JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`
WHERE pt.`some_value` = 'some comparison value'
AND EXISTS
( SELECT *
FROM product_categories pc
WHERE pc.product_id = pc.product_id
AND pc.status = 1
)
GROUP BY p.`product_id`
ORDER BY SUM( pt.`some_other_numeric_value` ) DESC ;
Another way to circumvent this problem is - after the GROUP BY MainTable.pk - to use DISTINCT inside the COUNT() or GROUP_CONCAT() aggregate functions. This works but you can't use it with SUM(). So, it's not useful in your specific query.
A third option - which works always - is to first group by the two (or more) side tables and then join to the main table. Something like this in your case:
SELECT p.* ,
COALESCE(pt.sum_other_values, 0) AS sum_other_values
COALESCE(pt.cnt, 0) AS tags_count,
COALESCE(pc.cnt, 0) AS categories_count,
COALESCE(category_titles, '') AS category_titles
FROM `product` p
JOIN
( SELECT product_id
, COUNT(*) AS cnt
, GROUP_CONCAT(title) AS category_titles
FROM `product_categories` pc
WHERE status = 1
GROUP BY product_id
) AS pc
ON p.`product_id` = pc.`product_id`
JOIN
( SELECT product_id
, COUNT(*) AS cnt
, SUM(some_other_numeric_value) AS sum_other_values
FROM `product_tags` pt
WHERE some_value = 'some comparison value'
GROUP BY product_id
) AS pt
ON p.`product_id` = pt.`product_id`
ORDER BY sum_other_values DESC ;
The COALESCE() are not strictly needed there - just in case you chnage the inner joins to LEFT outer joins.
you cant order by a sum function
instead you could do it like that
SELECT * ,SUM( pt.`some_other_numeric_value` ) as sumvalues
FROM `product` p
JOIN `product_categories` pc
ON p.`product_id` = pc.`product_id`
JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`
WHERE pt.`some_value` = 'some comparison value'
GROUP BY p.`product_id`
HAVING SUM( pc.`status` ) > 0
ORDER BY sumvalues DESC