mysql LEFT join for right table max value - mysql

I want to select every photo with only one comment and I want that comment to be the one with the maximum ID
I have tried following:
SELECT
p.id,
p.title,
MAX(c.id),
c.comment
FROM tb_photos AS p
LEFT JOIN tb_comments AS c ON p.id=c.photos_id.
It seems to be working, but I am wondering if there is a better way to do this?

you need to apply the max( comment ID ) on each photo (assuming the comment ID is auto-increment and thus always the most recent added to the table)
select
p.*,
tbc.Comment
from
tb_photos p
LEFT JOIN ( select c.photos_id,
max( c.id ) lastCommentPerPhoto
from
tb_comments c
group by
c.photos_id
order by
c.Photos_id ) LastPhotoComment
on p.id = LastPhotoComment.photos_id
LEFT JOIN tb_comments tbc
on LastPhotoComment.LastCommentPerPhoto = tbc.id

You can also do this with a cross join:
select
p.*,
LastPhotoComment.Comment
from
tb_photos p
cross join ( select top 1 c.Comment
from
tb_comments c
where
c.photos_id = p.id
order by
c.id DESC ) LastPhotoComment

Related

GROUP BY and ORDER BY issues

I have the following query:
SELECT DISTINCT (
s.styleTitle
), COUNT(p.id) AS `PictureCount`
FROM `style` s
LEFT JOIN `instagram_picture_style` ps ON s.id = ps.style_id
LEFT JOIN `instagram_shop_picture` p ON ps.picture_id = p.id
LEFT JOIN `instagram_picture_category` c ON c.picture_id = p.id
LEFT JOIN `instagram_second_level_category` sl ON c.second_level_category_id = sl.id
WHERE sl.id =25
GROUP BY p.id
ORDER BY PictureCount
however this query gives me:
I basically wanted the list to be ordered by the style that has the most pictures in it. What did I do wrong? Why is it giving me 1 on all of the styles, I am pretty sure it has more pictures for that style
ORDER BY doesn't have underscores. But equally important, you are using DISTINCT in a way where you seem to think that it is a function. It is not. It is a modifies on the SELECT and it applies to all columns.
You should group by the same column you have in the distinct. Something like this:
SELECT s.styleTitle, COUNT(p.id) AS `PictureCount`
FROM `style` s
LEFT JOIN `instagram_picture_style` ps ON s.id = ps.style_id
LEFT JOIN `instagram_shop_picture` p ON ps.picture_id = p.id
LEFT JOIN `instagram_picture_category` c ON c.picture_id = p.id
LEFT JOIN `instagram_second_level_category` sl ON c.second_level_category_id = sl.id
WHERE sl.id = 25
GROUP BY s.styleTitle
ORDER BY PictureCount DESC;
In fact, you almost never need distinct with group by. If you are using, you need to think why it would be necessary.

Delete an SQL query result set

I'm trying to delete an SQL result set but it won't work:
DELETE FROM votes
WHERE id IN (
SELECT *
FROM votes v
LEFT JOIN comments c ON f.id = v.post_id
GROUP BY v.id
HAVING COUNT(c.comment) = 0 )
It's true, that you can't use the same table from which you want to delete rows in a direct subselect, but with a little trick - a subselect on a subselect as derived table - you can do it:
DELETE FROM votes
WHERE id IN (
SELECT
t.id
FROM (
SELECT v.id, COUNT(c.comment) cnt
FROM votes v
LEFT JOIN comments c ON f.id = v.post_id
GROUP BY v.id
HAVING COUNT(c.comment) = 0
) t
);
I'm assuming that the rows without comments should be deleted.
You are close...2 changes
a subquery in a where in() statement can only return one field. Change select * to select v.id
having count = 0 doesn't quite work in a logical sense. If count = 0 then it's not there to delete anyway. I suspect with the left join syntax you've used, you are going for votes that have 0 comments? Right idea with the left join, but you want where c.comment is null (left join produces nulls...where c.comment is null means there was no comment found).
Of course this won't work due to mysql:
DELETE FROM votes
WHERE id IN (
SELECT v.id
FROM votes v
LEFT JOIN comments c ON f.id = v.post_id
where c.comments is null)
If I was stuck in MySQL...(sorry this is psuedo code, I haven't been in mysql long enough to get this exact in a text window.
select id
into #temp
FROM votes v
LEFT JOIN comments c ON f.id = v.post_id
where c.comments is null
delete from votes where id in (select id from #temp)
drop table #temp
Seems like a silly work around

how to do inner join on the first row of the table

I have the following query:
SELECT p.`id` , p.`name` , pp.`name`
FROM `product` p
INNER JOIN `product_picture` pp ON p.id = pp.product_id
and then this gives me the following result:
However I only wanted to get only one product picture for each item (which is the first one on top). So I wanted to get a query that returns:
7 Glavins Necklace 83081c5619068f6ec700e12d827d2199745c8283.jpeg
8 Woodsie Bracelet 83081c5619068f6ec700e12d827d2199745c8283.jpeg
How do I do so?
Use an aggregate MIN() or MAX() to pick one picture
SELECT p.`id` , p.`name` , MIN(pp.`name`) picture
FROM `product` p INNER JOIN `product_picture` pp
ON p.id = pp.product_id
GROUP BY p.`id` , p.`name`

SQL - Ambiguous Column

I can't see what is wrong with this query. I get an error saying:
"column article_id in from clause is ambiguous"
I understand that it may have something to do with table name aliases but not sure of how to fix. If the query was smaller I may be able to work something out but it's pretty confusing to me and every time I change something to try and fix it, something else stops - so I thought I'd ask first.
SELECT bt.topic_title, f.article_id, p.photo_id, ba.title, ba.slug,
IFNULL(c.cnt,0) comments, IFNULL(ph.cnt,0) photos, IFNULL(v.cnt,0) videos
FROM blog_article_followers AS f
LEFT OUTER JOIN (
SELECT article_id, COUNT(comment_id) as cnt
FROM blog_comments
GROUP BY article_id) c
ON f.article_id = c.article_id
LEFT OUTER JOIN (" _
SELECT article_id, COUNT(photo_id) as cnt
FROM photos
GROUP BY article_id) ph
ON f.article_id = ph.article_id
LEFT OUTER JOIN (
SELECT article_id, COUNT(video_id) as cnt
FROM videos
GROUP BY article_id) v
ON f.article_id = v.article_id
LEFT JOIN blog_topics bt ON f.topic_id = bt.topic_id
LEFT JOIN blog_articles AS ba USING (article_id)
LEFT JOIN photos AS p USING (article_id)
WHERE f.member_id = 100 AND p.cover = 1
ORDER BY f.follow_date DESC;
Try replacing this:
LEFT JOIN blog_articles AS ba USING (article_id)
LEFT JOIN photos AS p USING (article_id)
With this
LEFT JOIN blog_articles AS ba ON f.article_id = ba.article_id
LEFT JOIN photos AS p ON f.article_id = photos.article_id
you have to rename the column
LEFT JOIN photos AS p USING (p.article_id)
or to whichever table article_id belongs to

MySQL LEFT JOIN, GROUP BY and ORDER BY not working as required

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)