I have 2 tables:
products:
- id
- name
product_images:
- id
- image
- product_id
I'm doing the following query:
SELECT p.*, i.image
FROM products p
LEFT JOIN product_images i
ON p.id = i.product_id`
ORDER BY created_at DESC
However if a product has a few images, then this product row is duplicated. How can i remove these duplicates, by showing only the 1st match in a pair of p.id = i.product_id
For this dataset, simple aggregation should do it:
SELECT p.*, min(i.image)
FROM products p
LEFT JOIN product_images i ON p.id = i.product_id
GROUP BY <enumerate all columns from products here>
ORDER BY created_at DESC
LIMIT ${limit}
If you want more colums from product_images, then you can also filter with a correlated subquery; assuming that product_images has primary key id, that would look like:
SELECT p.*, i.image
FROM products p
LEFT JOIN product_images i
ON i.id = (SELECT MIN(i1.id) FROM product_images i1 WHERE i1.product_id = p.id)
ORDER BY created_at DESC
LIMIT ${limit}
If image's data type is varchar or int then instead of joining the table product_images, join to the MIN of the images of each product:
SELECT p.*, i.image
FROM products p
LEFT JOIN (
SELECT product_id, MIN(image) image
FROM product_images
GROUP BY product_id
) i
ON p.id = i.product_id
ORDER BY created_at DESC
LIMIT ${limit}
Related
I have two tables:
products table: id, name, price
prices table: id, product_id, group_id, price
Query:
SELECT p.id, p.name, p.price, pr.price AS newPrice, pr.id AS prID
FROM products p
LEFT JOIN prices pr ON pr.product_id = p.id
WHERE pr.group_id = '1'
ORDER BY p.id ASC
I need see all products and if filled prices than get result from prices table.
But I see only filled datas, when I'm writing "WHERE"
Sorry for my English
When using LEFT JOINThe filter on the prices table should be placed at the ON clause
SELECT p.id, p.name, p.price, pr.price AS newPrice, pr.id AS prID
FROM products p
LEFT JOIN prices pr ON pr.product_id = p.id AND pr.group_id = 1
ORDER BY p.id ASC
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;
i have 3 tables:
table_products - product_id, pname
variants - vid, vname
table_product_varients - product_id, vid
i want to get the count for product variants for all the products, and if there is no variant for a product it should give count as 0.
This is my query:
SELECT P.product_id, count(*) AS count
FROM table_product_varients AS PV
LEFT JOIN table_products AS P ON PV.product_id = P.product_id
GROUP BY P.product_id
ORDER BY P.product_id ASC
But this is not giving products for which there is no variants.
can any one help me on this?
You should have put the table_products table to the left.
Also you should have counted PV.product_id.
SELECT
P.product_id,
count(PV.product_id) AS count
FROM table_products AS P
LEFT JOIN table_product_varients AS PV ON PV.product_id = P.product_id
GROUP BY P.product_id
ORDER BY P.product_id ASC;
Note: For those products which don't have corresponding entry in table_products_varients table you will get NULL value of PV.product_id. Thus COUNT(NULL) actually returns 0
Some subtleties regarding COUNT:
SELECT COUNT(0); Result: 1
SELECT COUNT(-1); Result: 1
SELECT COUNT(NULL); Result: 0
SELECT COUNT(71); Result: 1
SQL FIDDLE
I have a table of products:
CREATE TABLE products (`id` INT);
And a table of images for those products:
CREATE TABLE images (`id` INT, `product_id` INT, `default` TINYINT(1));
I need to select all the products, and join the images table so that images with (default = 1) will be preferred, and if a product has no images with (default = 1), an image with (default = 0) will be shown in its place.
Here's an image showing what I'm looking for:
Right now I have this query:
SELECT p.id, i.id
FROM products AS p
LEFT JOIN (
SELECT product_id, url
FROM images
ORDER BY default
) AS i
ON p.id = i.product_id
GROUP BY p.id
ORDER BY p.name
Which doesn't prioritize "default" images. The subquery doesn't seem to do anything.
SQLFiddle demo
select products.id,
coalesce(t1.mid,t2.mid) as image_id
from products
left join (select min(id) mid,product_id
from images where `default`=1
group by product_id ) t1
on products.id=t1.product_id
left join (select min(id) mid,product_id
from images where `default`=0
group by product_id ) t2
on products.id=t2.product_id
Looks like I was just missing a 'DESC' in the subquery's ORDER BY
:\
If you want to show only one image for product, then try this query -
SELECT p.*, i.* FROM products p
JOIN (SELECT * FROM images ORDER BY `default` DESC) i
ON p.id = i.product_id
GROUP BY p.id
Try this:
SELECT
p.id productid,
IFNULL(i1.id, i2.id)
FROM products AS p
LEFT JOIN images AS i1 ON p.id = i1.product_id
AND i1.`Default` = 1
LEFT JOIN images AS i2 ON p.id = i2.product_id
AND i2.`Default` = 0
GROUP BY p.id;
I have a table
'products' => ('product_id', 'name', 'description')
and a table
'product_price' => ('product_price_id', 'product_id', 'price', 'date_updated')
I want to perform a query something like
SELECT `p`.*, `pp`.`price`
FROM `products` `p`
LEFT JOIN `product_price` `pp` ON `pp`.`product_id` = `p`.`product_id`
GROUP BY `p`.`product_id`
ORDER BY `pp`.`date_updated` DESC
As you can probably guess the price changes often and I need to pull out the latest one. The trouble is I cannot work out how to order the LEFT JOINed table. I tried using some of the GROUP BY functions like MAX() but that would only pull out the column not the row.
Thanks.
It appears that it is impossible to use an ORDER BY on a GROUP BY summarisation. My fundamental logic is flawed. I will need to run the following subquery.
SELECT `p`.*, `pp`.`price` FROM `products` `p`
LEFT JOIN (
SELECT `price` FROM `product_price` ORDER BY `date_updated` DESC
) `pp`
ON `p`.`product_id` = `pp`.`product_id`
GROUP BY `p`.`product_id`;
This will take a performance hit but as it is the same subquery for each row it shouldn't be too bad.
You need to set aliases properly I think and also set what you are joining on:
SELECT p.*, pp.price
FROM products AS p
LEFT JOIN product_price AS pp
ON pp.product_id = p.product_id
GROUP BY p.product_id
ORDER BY pp.date_updated DESC
This will give you the last updated price:
select
p.*, pp.price
from
products p,
-- left join this if products may not have an entry in prodcuts_price
-- and you would like to see a null price with the product
join
(
select
product_price_id,
max(date_updated)
from products_price
group by product_price_id
) as pp_max
on p.product_id = pp.product_id
join products_price pp on
pp_max.prodcuts_price_id = pp.products_price_id
Mysqlism:
SELECT p.*, MAX(pp.date_updated), pp.price
FROM products p
LEFT JOIN product_price pp ON pp.product_id = p.product_id
GROUP BY p.product_id
Will work on some RDBMS:
SELECT p.*, pp.date_updated, pp.price
FROM products p
LEFT JOIN product_price pp ON pp.product_id = p.product_id
WHERE (p.product_id, pp.date_updated)
in (select product_id, max(date_updated)
from product_price
group by product_id)
Will work on most RDBMS:
SELECT p.*, pp.date_updated, pp.price
FROM products p
LEFT JOIN product_price pp ON pp.product_id = p.product_id
WHERE EXISTS
(
select null -- inspired by Linq-to-SQL style :-)
from product_price
WHERE product_id = p.product_id
group by product_id
HAVING max(date_updated) = pp.date_updated
)
Will work on all RDBMS:
SELECT p.*, pp.date_updated, pp.price
FROM products p
LEFT JOIN product_price pp ON pp.product_id = p.product_id
LEFT JOIN
(
select product_id, max(date_updated) as recent
from product_price
group by product_id
) AS latest
ON latest.product_id = p.product_id AND latest.recent = pp.date_updated
And if nate c's code intent is to just get one row from product_price, no need to table-derive (i.e. join (select product_price_id, max(date_updated) from products_price) as pp_max), he might as well just simplify(i.e. no need to use the product_price_id surrogate primary key) it like the following:
SELECT p.*, pp.date_updated, pp.price
FROM products p
LEFT JOIN product_price pp ON pp.product_id = p.product_id
WHERE pp.date_updated = (select max(date_updated) from product_price)