Remove duplicate product with lower price using mysql query - mysql

I have many duplicate products that I need to delete. Is there a query to delete these "Duplicates" with the "lower price" and/or "Same Price" an just keep 1 of each product?
The duplicates have duplicate "Product Name" I am using Opencart Version 2.1.0.1

Since you are using mysql, you need to use joins (partition by is not supported):
The select:
select p.*
from products as p
join
(
select name, min(price) as price
from products group by name having count(price) = 2
) as p2 on p2.name = p.name and p2.price = p.price;
Gets the lowest price for all duplicate products (where duplicate assumes there are exactly two rows of the same product).
To delete, change the initial select to a delete, as follows:
delete p.*
from products as p
join
(
select name, min(price) as price
from products group by name having count(price) = 2
) as p2 on p2.name = p.name and p2.price = p.price;

According to the schema on http://wiki.opencarthelp.com/doku.php?id=databse_schema and only one language the folowing query should solve your issue:
delete p1
from product p1
join product_description d1 on d1.product_id = p1.product_id
join product_description d2
on d2.product_id <> d1.product_id
and d2.language_id = d1.language_id
and d2.name = d1.name
join product p2 on p2.product_id = d2.product_id
where d1.language_id = 1 -- define the language used for product name
and (p2.price > p1.price -- delete if higher price exists
or p2.price = p1.price and p2.product_id < p1.product_id -- delete if same price with lower id exists
)
;

Without much info (which RDBMS, etc) I can only speculate this solution would work for you:
WITH Flagged AS
(
SELECT
ProductName,
CASE WHEN ROW_NUMBER() OVER(PARTITION BY ProductName ORDER BY Price DESC) = 1 THEN 0 ELSE 1 END AS Delete
FROM
Products
)
DELETE p
FROM Products p
JOIN Flagged f
ON (p.ProductName = f.ProductName)
WHERE f.Delete = 1;

Related

SQL doesn't see alias

SELECT name,
manufacturer,
prize
FROM products AS p
GROUP BY manufacturer
HAVING prize = (
SELECT Max(prize)
FROM products p1
WHERE p.`id-product` = p1.`id-product`
GROUP BY p1.manufacturer DESC
LIMIT 1
)
And the error is:
#1054 - Unknown column 'shop.p.id.product' in 'where clause'
I think you need correlated subquery instead :
SELECT p.name, p.manufacturer, p.prize
FROM products AS p
WHERE p.prize = (SELECT MAX(p1.prize) FROM products p1 WHERE p1.manufacturer = p.manufacturer);
By this way, you will get manufacturers with highest price.
EDIT : If one product has same prize then you will need PK (Primary/Identity) column that specify unique sequence :
SELECT p.name, p.manufacturer, p.prize
FROM products AS p
WHERE p.pk = (SELECT p1.pk
FROM products p1
WHERE p1.manufacturer = p.manufacturer
ORDER BY p1.prize DESC
LIMIT 1
);
If you are running with latest version of MySQL, then you can also use ranking function :
SELECT p.*
FROM (SELECT p.name, p.manufacturer, p.prize,
ROW_NUMBER() OVER (PARTITION BY p.manufacturer ORDER BY p.prize DESC) AS Seq
FROM products AS p
) p
WHERE Seq = 1;
Your query has nothing of that form. I would recommend writing the query by qualifying all column references:
SELECT p.name, p.manufacturer, p.prize
FROM products p
GROUP BY p.manufacturer
HAVING p.prize = (SELECT MAX(p1.prize)
FROM products p1
WHERE p.`id-product` = p1.`id-product`
GROUP BY p1.manufacturer DESC
LIMIT 1
);
MySQL allows the syntax with extra columns in the SELECT that are not in the GROUP BY, so that would not (normally) generate an error. It is not correct, though, by the rules of SQL.
I'm not sure what the subquery is supposed to be doing. If you want the manufacturers with the highest price, then you would not use the GROUP BY in the subquery.
If you want the highest priced product for each manufacturer:
SELECT p.name, p.manufacturer, p.prize
FROM products p
HAVING p.prize = (SELECT MAX(p1.prize)
FROM products p1
WHERE p.manufacturer = p1.manufacturer
LIMIT 1
);

UNION MySQL, Check if its bold

Hello guys i have that SQL:
SELECT p.* FROM products p WHERE required_product_id IS NULL
UNION ALL
SELECT p.* FROM products p, orders o WHERE p.required_product_id = o.product_id
AND o.user_id = 1
UNION DISTINCT
SELECT p.`*` FROM products p, orders o WHERE p.id NOT IN (SELECT product_id FROM orders WHERE product_id = p.id AND o.user_id = 1)
AND p.max_buys = 1;
This query first checking if item is purchased and show next item! i want to check if user is purchased that product to return only that product that user is not bought it
table structure = Products: http://prntscr.com/k6ogp4 ,Orders: http://prntscr.com/k6ogrz
max_buys colum on products (if it 1 it can buy it once , if its 0 it can be buyed many times)
In your case I prefer to have a flexible query to manage requirements and also I think that UNION is not required in this case [if your description is complete].
SELECT w.* from (
SELECT
(SELECT count(o.product_id) FROM orders o WHERE o.product_id = p.id AND o.user_id = 1) bought_count,
(SELECT count(q.product_id) FROM orders q WHERE q.product_id = p.required_product_id AND q.user_id = 1) order_depend,
p.*
FROM products p ) w
where
(order_depend>0 or required_product_id is null) and -- unlock order depended products
(max_buys=0 or -- can buy more than once
bought_count=0) -- or not bought yet
order by
order_depend desc, -- dependent products to ordered products in first level
bought_count asc, -- not bought products in second level
recommended desc -- recommended products in third level
You can also manage any other order according to your requirement.

LEFT JOIN returns NULL if there are just one column in table

I try to get the latest created product price. Every Product is unique but can have different prices. However, my query only works if a product have more than a price as row in the product_price table:
This is my query:
SELECT
i.name AS title,
i.id AS product_id,
m.name AS manufacturer,
image,
price_sales,
price_new,
price_used,
price_old
FROM product_info as i
LEFT JOIN product_manufacturer AS m ON i.manufacturer_id = m.id
LEFT JOIN (SELECT * FROM product_price ORDER BY created_at DESC LIMIT 1) AS p ON i.id = p.id_product
WHERE category_id = 2
AND i.is_deactivated IS NULL
LIMIT 0, 20;
I just need the latest created price row.
Result
The problem you have is that the subquery:
(SELECT * FROM product_price ORDER BY created_at DESC LIMIT 1)
Does not get the latest price per product, but simply the latest price, so will only ever return one row, meaning only one of your products will actually have a price.
The way to resolve this is to remove any prices where a newer one exists, so for simplicity if you look just at the price table, the following will give you only the latest product prices:
SELECT p.*
FROM product_price AS p
WHERE NOT EXISTS
( SELECT 1
FROM product_price AS p2
WHERE p2.id_product = p.id_product
AND p2.created_at > p.created_at
);
However, MySQL will optmise LEFT JOIN/IS NULL better than NOT EXISTS (although I think the former conveys intention better), so a more efficient approach would be:
SELECT p.*
FROM product_price AS p
LEFT JOIN product_price AS p2
ON p2.id_product = p.id_product
AND p2.created_at > p.created_at
WHERE p2.id IS NULL;
Finally, introducing this back to your main query, you would end up with:
SELECT i.name AS title,
i.id AS product_id,
m.name AS manufacturer,
i.image,
p.price_sales,
p.price_new,
p.price_used,
p.price_old
FROM product_info as i
LEFT JOIN product_manufacturer AS m
ON m.id = i.manufacturer_id
LEFT JOIN product_price AS p
ON p.id_product = i.id
LEFT JOIN product_price AS p2
ON p2.id_product = p.id_product
AND p2.created_at > p.created_at
WHERE i.category_id = 2
AND i.is_deactivated IS NULL
AND p2.id IS NULL
LIMIT 0, 20;

MYSQL NOT IN with multiple column names

i have 2 mysql queries,
1 is to select the all branch stocks and other is to select stock count lists.
i need to select all the branch stocks which is not included in stock count lists( by considering stock barcode and stock batch)
my first query to select all branch stock is
SELECT SQL_CALC_FOUND_ROWS stock_id ,product_name , stock_batch,stock_barcode
FROM stock INNER JOIN product ON product_id = stock_product
LEFT JOIN packing ON product_package=packing_id
WHERE stock_branch = 2 AND stock_available_flg = 1
group by stock_id
and second query to select counted stock lists is
SELECT SQL_CALC_FOUND_ROWS stock_id,stock_barcode, product_name,stock_batch FROM stock_count_item
INNER JOIN stock ON stock_id = stock_count_item_stock
INNER JOIN product ON stock_product = product_id
INNER JOIN category ON category_id = product_category
WHERE stock_count_item_stock_count = 42 GROUP BY stock_barcode
what i need is to select all branch stock which is not included in stock count lsts with same barcode and batch
my query is
SELECT SQL_CALC_FOUND_ROWS stock_id ,product_name , stock_batch,stock_barcode
FROM stock
INNER JOIN product ON product_id = stock_product
LEFT JOIN packing ON product_package=packing_id
WHERE stock_branch = 2 AND stock_available_flg = 1 AND ( stock_batch,stock_barcode)
not in
(SELECT stock_batch ,stock_barcode FROM stock_count_item
INNER JOIN stock ON stock_id = stock_count_item_stock
INNER JOIN product ON stock_product = product_id
INNER JOIN category ON category_id = product_category WHERE stock_count_item_stock_count = 42)
but this not gives the correct result .How am consider stock_batch,stock_barcode together to correct this?
Use NOT EXISTS instead of NOT IN
BUT because you have not identified which tables each column comes from so I (or anyone maintaining your code) cannot learn a column's source from reading the query. ALWAYS prefix the column by the table (or table's alias).
SELECT
SQL_CALC_FOUND_ROWS stock_id
, product_name
, stock_batch
, stock_barcode
FROM stock
INNER JOIN product ON product_id = stock_product
LEFT JOIN packing ON product_package = packing_id
WHERE stock_branch = 2
AND stock_available_flg = 1
NOT NOT EXSTS (
SELECT 1
FROM stock_count_item
INNER JOIN stock ON stock_id = stock_count_item_stock
INNER JOIN product ON stock_product = product_id
INNER JOIN category ON category_id = product_category
WHERE stock_count_item_stock_count = 42
and outer_alias.stock_batch = inner_alias.stock_batch
and outer_alias.stock_barcode = inner_alias.stock_barcode
)
;
I have tried to indicate what is required by using
and outer_alias.stock_batch = inner_alias.stock_batch
the "outer_alias" is an alias (or tablename) from before the NOT EXISTS subquery, and the inner_alias is the reverse. At least one of these has to be an alias if you choose to use a tablename for one of hem.
e.g.
select ... from stock where not exists (... from stock s and stock.id = s.id)
"stock" is the "outer_alias" of stock.id
"s" is the "inner_alias" of s.id
the final query with aliases (via comment below)
SELECT
SQL_CALC_FOUND_ROWS stock_id
, product_name
, s.stock_batch --<< please please please use aliases everywhere
, s.stock_barcode
FROM stock s
INNER JOIN product ON product_id = stock_product
LEFT JOIN packing ON product_package = packing_id
WHERE stock_branch = 2
AND stock_available_flg = 1
AND NOT EXISTS (
SELECT
1
FROM stock_count_item
INNER JOIN stock st ON stock_id = stock_count_item_stock
INNER JOIN product ON stock_product = product_id
INNER JOIN category ON category_id = product_category
WHERE stock_count_item_stock_count = 42
AND s.stock_batch = st.stock_batch
AND s.stock_barcode = st.stock_barcode
)

Select from 3 tables with two order by before two group by

I try to get a list of products with each newest and lowest offer price
Table product:
id | name
Table offer:
id | product_id | price | created | dealer_id
Table invalids:
id | offer_id | status
I have tried:
SELECT * FROM product INNER JOIN
(
SELECT offer.product_id , offer.price
FROM offer
LEFT JOIN invalids
ON offer.id = invalids.offer_id
WHERE invalids.id IS NULL
GROUP BY offer.dealer_id
ORDER BY offer.created DESC
) o
ON o.product_id = product.id
ORDER BY product.name
I have tried an sqlfiddle http://sqlfiddle.com/#!9/32658/3 with this offer values:
(`id`, `price`, `dealer_id`, `product_id`, `created`)
(1,12.60,1,1,'2015-05-17 08:44:45'),
(2,13.00,1,1,'2015-08-17 08:44:45'),
(3,20.00,1,1,'2015-08-17 08:45:30'),
(4,10.00,1,1,'2015-08-17 08:45:46'),
(5,4.00,2,1,'2015-05-17 08:44:11'),
(6,11.00,2,1,'2015-08-17 08:44:46'),
(7,5.00,2,1,'2015-08-17 08:45:31'),
(9,110.00,2,2,'2015-08-17 08:46:58'),
(10,11.00,2,2,'2015-08-17 08:47:12');
Expected value for product ID 1 is offer ID 7 with price 5.
These steps I think I must realize:
Order offers by created and group by dealer_id to get newest entries
Take result from step 1 and order it by price to get smallest price.
Make this for all products
Maybe I must use a second SELECT FROM offer with GROUP BY and ORDER BY but how do I get I the product_id from the first (outer) select?
Well I would start by getting the latest date for each product offer like this:
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id;
Once you have that, you can join it to the original table to get that offer:
SELECT o.*
FROM offer o
JOIN(
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id) tmp ON tmp.product_id = o.product_id AND tmp.latestOffer = o.created;
Here is an SQL Fiddle example.
This query should help you:
SELECT *
FROM product
JOIN (
SELECT product_id, min(price) as minPrice, max(created) as newestOffer
FROM offer
WHERE id NOT IN (SELECT offer_id FROM invalids)
GROUP BY 1
) as b
ON product.id = b.product_id
A shot in the dark based on what I understand you to be after...
lots of nested subqueries.. keep thinking there's got to be a better way...
SELECT OO.ID, OO.Price, OO.Dealer_Id, OO.Product_ID, OO.created, P.name
FROM Offer OO
INNER JOIN (
SELECT Min(Price) as MinP
FROM offer O
INNER JOIN (
SELECT max(OI.created) as LatestOffer, OI.Dealer_ID, OI.Product_ID
FROM Offer OI
LEFT JOIN invalids I
on OI.Id = I.offer_Id
WHERE I.ID is null
GROUP BY OI.Dealer_Id, OI.Product_Id
) B
on O.Dealer_Id = B.Dealer_Id
and O.Product_Id = B.Product_Id
and O.Created = B.LatestOffer
) Z
on OO.Price = Z.MinP
INNER JOIN product P
on P.ID = OO.Product_ID
SQL FIDDLE