MySQL: Is a subquery the right move? - php

I have several products each with several images.
I am trying to display 3 products with only 1 of their corresponding images.
With the code below i get 3 different products with only 1 image being successfully displayed.
If i take off the sub-query LIMIT 1 i get 3 of the same product just with different images.
Any help would be greatly appreciated.
SELECT pro.id,
pro.title AS product_title,
pro.price,
img.image,
img.title AS image_title,
FROM products AS pro
LEFT JOIN
(SELECT product_id,image_library_id
FROM images_products
LIMIT 1) AS ips
ON ips.product_id = pro.id
LEFT JOIN image_library AS img
ON img.id = ips.image_library_id
WHERE pro.status_id='1'
LIMIT 3

Your question-title mentions a "correlated subquery", but this actually isn't a correlated subquery; it's a regular (uncorrelated) subquery that you're joining to. So that subquery is performed first, before the join, and only returns one record from images_products at all. You'd therefore get at most one working image. I believe that the query you want is this:
SELECT pro.id,
pro.title AS product_title,
pro.price,
img.image,
img.title AS image_title
FROM products AS pro
LEFT
JOIN image_library AS img
ON img.id =
( SELECT image_library_id
FROM images_products AS ips
WHERE ips.product_id = pro.id
LIMIT 1
)
WHERE pro.status_id = '1'
LIMIT 3
;
using a true correlated subquery. (Tested.)

Related

Laravel get n results per group

I have two tables : tbl_properties and tbl_property_images. I need to select latest 3 images of each property along with property_id.
I tried with sub query with limit
DB::table('properties as p')
->leftjoin(DB::raw("(select property_id, property_image_id, image
from tbl_property_image
where property_image_status = 1
group by property_id
having count(*) = 3) as tbl_imgtemp") , 'imgtemp.property_id', '=', 'p.property_id')
->where('property_status',1)
->get();
This returns total 3 records. please help me how can i do that.
How can I select 3 images per property?
You use following query to get 3 images per property, I have used id column of images table to pick latest images assuming id column is set as auto increment
SELECT p.*,i.*
FROM properties p
JOIN(SELECT i1.property_id,i1.property_image_status,i1.image
FROM tbl_property_image i1
LEFT OUTER JOIN tbl_property_image i2
ON (i1.property_id = i2.property_id AND i1.property_image_id < i2.property_image_id)
WHERE i1.property_image_status = 1
GROUP BY i1.property_id,i1.property_image_status,i1.image
HAVING COUNT(*) < 3
) i
ON p.property_id = i.property_id
DEMO
Another approach using group_concat() if you need only a single column from images table
SELECT p.property_id , p.title,SUBSTRING_INDEX(GROUP_CONCAT(i.image ORDER BY i.property_image_id DESC),',',3) images
FROM properties p
JOIN tbl_property_image i ON p.property_id = i.property_id
WHERE i.property_image_status = 1
GROUP BY p.property_id , p.title
But this solution has some limitations, As per docs The result is truncated to the maximum length that is given by the group_concat_max_len system variable, which has a default value of 1024. The value can be set higher, although the effective maximum length of the return value is constrained by the value of max_allowed_packet
DEMO
I have used sample schema and data set for above queries you have to adjust these as per your needs

SQL - Get AVG value from other table

My question sounds really easy, but I'm stuck.
Sample Data:
Listing:
id title State
1 Hotel with nice view Arizona
2 Hotel to stay Arizona
Review:
id listing_id rating mail_approved
1 1 4(stars) 1
2 1 4(stars) 0
3 1 3(stars) 1
4 2 5(stars) 1
So now I get the AVG value of the listings, but I want to get only the value of each listing when the review is mail_approved = 1. But when there is none review or no review with mail_approved = 1 it should give me the listing back just with 0.0 review points. So I would like to get all listing back if they have a review just calculate the AVG of those reviews with mail_approved = 1
How can I do this?
Do I have to rewrite the whole query?
Here is my query:
SELECT
ls.id,
title,
state,
ROUND(AVG(rating),2) avg_rating
FROM listing ls
JOIN review rv
ON ls.id = rv.listing_id
WHERE ls.state = '$get_state'
GROUP BY ls.id,
title,
state
ORDER BY avg_rating DESC
You used join, which is short for inner join. This type of join only gives results if a matching record exists in both tables. Change it to left join (short for left outer join), to also include listings without reviews.
You will need to move the state check and any other check to the join condition too, otherwise those listings without review will be dropped from the result again.
Lastly, you can coalesce the average value to get 0 instead of null for those records.
SELECT
ls.id,
title,
state,
COALESCE(ROUND(AVG(rating),2), 0) avg_rating
FROM listing ls
LEFT JOIN review rv
ON ls.id = rv.listing_id
AND ls.state = '$get_state'
AND ls.mail_approved = 1
GROUP BY ls.id,
title,
state
ORDER BY avg_rating DESC
As a side note, please check prepared statements (for PDO or MySQLi) for the proper way to pass input parameters to your query instead of concatenating with variables like $get_state. Concatting is error prone, and makes you more vulnerable for SQL injection.
Outer join the avarage ratings to the hotels:
select
l.id,
l.title,
l.state,
coalesce(r.avg_rating, 0)
from listing l
left join
(
select
listing_id,
round(avg(rating), 2) as avg_rating
from review
where mail_approved = 1
group by listing_id
) r on r.listing_id = l.id
where l.state = '$get_state'
order by avg_rating desc;

Select random rows from one MYSQL table with join

Afternoon folks,
I have had a good dig around and can't find the answer, so a good time to ask!
I'd like to select random rows from one table and then join into this random rows from another table where the ID that I have is the same. It would also be great to only select where I have an entry in the second table. I have tried all manner of sub-queries but am getting a bit lost. An inner join as read will do it but again with the randomness of it all!! Grrr...
SELECT
tracks.track_id,
cuttings.square_cutting,
cuttings.cutting_2,
cuttings.cutting_3,
cuttings.blog_text
FROM tbl_tracks tracks,
(SELECT
square_cutting,
cutting_2,
cutting_3,
blog_text
FROM
tbl_cuttings
WHERE track_id = tracks.track_id <-- wont find it, obviously!!
ORDER BY RAND()
LIMIT 1) cuttings
WHERE tracks.active = '1' ORDER BY RAND()
Thanks in advance for any help.
So:
I'd like random tracks showing
track id -> with random cuttings, of which there can be many but I just want 1.
It would then be ideal to only show a result if there is a cutting associated with that track.
Hope that helps.
I'm now trying to go a step further with this and order this by a RAND() seed as I'm now having to add in pagination. Only problem is that its not giving me back the same random list due to a given seed. Any Ideas?
SELECT
tracks.track_id,
cuttings.square_cutting,
cuttings.cutting_2,
cuttings.cutting_3,
cuttings.blog_text
FROM tbl_tracks tracks
INNER JOIN
(SELECT track_id,
square_cutting,
cutting_2,
cutting_3,
blog_text
FROM
tbl_cuttings
ORDER BY RAND()) cuttings ON tracks.track_id = cuttings.track_id
WHERE tracks.active = '1'
ORDER BY RAND(1)
LIMIT 0,4;
you could use an inner join
SELECT
tracks.track_id,
cuttings.square_cutting,
cuttings.cutting_2,
cuttings.cutting_3,
cuttings.blog_text
FROM tbl_tracks tracks
INNER JOIN
(SELECT track_id,
square_cutting,
cutting_2,
cutting_3,
blog_text
FROM
tbl_cuttings
ORDER BY RAND()
LIMIT 1) cuttings on cuttings.track_id = tracks.track_id
WHERE tracks.active = '1'
ORDER BY RAND()

MySQL #1241 - Operand should contain 1 column(s) on counting

I am running this query, and I am getting ** #1241 - Operand should contain 1 column(s)** error:
SELECT `forumCategories`.`id`, `forumCategories`.`name`, `forumCategories`.`order`, `forumCategories`.`description`, `forumCategories`.`date_created`, COUNT(forumPosts.forumCategory_id) as postCount,
(SELECT `forumPosts`.*, `forumChildPosts`.`id`, `forumChildPosts`.`forumPost_id`, COUNT(forumChildPosts.forumPost_id) as childCount FROM `forumChildPosts` LEFT JOIN `forumPosts` ON `forumPosts`.`id` = `forumChildPosts`.`forumPost_id` GROUP BY `forumPosts`.`id`) AS childCount
FROM `forumCategories`
LEFT JOIN `forumPosts` ON `forumCategories`.`id` = `forumPosts`.`forumCategory_id`
GROUP BY `forumCategories`.`id`
ORDER BY `forumCategories`.`order` DESC
I have 3 tables:
forumCategories
forumPosts | forumPosts.forumCategory_id = forumCategories.id
forumChildPosts | forumChildPosts.forumPosts_id = forumPosts.id
I want to count all posts for the forum category, and them I want to count all the child posts that belongs to that forum category. How can I do this?
You can't select several items with a subselect and then give them one name. Now you're getting everything from forumPosts, something from forumChildPosts etc and trying to put that into a single column, childCount. This is not allowed.
It might be enough to remove all other result columns from that select and only leave the count?
I couldn't try it, is that makes sense ? But you can't get nested results from mysql due to its limitation, MYSQL is a Matrix table.
SELECT `forumCategories`.`id`,
`forumCategories`.`name`,
`forumCategories`.`order`,
`forumCategories`.`description`,
`forumCategories`.`date_created`,
COUNT(forumPosts.forumCategory_id) AS postCount,
(SELECT COUNT(forumChildPosts.forumPost_id) AS childCount FROM `forumChildPosts` LEFT JOIN `forumPosts` ON `forumPosts`.`id` = `forumChildPosts`.`forumPost_id` GROUP BY `forumPosts`.`id`) AS childCount
FROM `forumCategories`
LEFT JOIN `forumPosts` ON `forumCategories`.`id` = `forumPosts`.`forumCategory_id`
GROUP BY `forumCategories`.`id`
ORDER BY `forumCategories`.`order` DESC

Mysql Join Won't Select

I have been doing a lots of research online and from my understanding i think my query is ok
That is why i need your help to point me out what im doing wrong.
What My Query Should Do
My query should fetch our stock level from both warehouse
Problem Is
if the product is not in both warehouse the query dont give any result.
Ok so first i have two database of warehouse stock level. that look like that.
Databases
-warehouse1
-warehouse2
Table
-product
Columns
-id
-SKU
-qty
So my Query is
SELECT
warehouse1.product.id as 1_id,
warehouse2.product.id as 2_id ,
warehouse1.product.SKU,
warehouse1.product.qty as 1_qty,
warehouse2.product.qty as 2_qty
FROM `warehouse1`.`product`
LEFT JOIN `warehouse2`.`product`
ON
(`warehouse1`.`product`.`SKU` = `warehouse2`.`product`.`SKU`)
WHERE
warehouse1.product.SKU = '$sku'
OR
warehouse2.product.SKU = '$sku'
ORDER BY
(1_qty + 2_qty) DESC
if i make the where clause like this
WHERE warehouse1.product.SKU = '$sku'
it is then working but i can't get stock from both warehouse.
What should i do if i want to receive the stock level from both warehouse even if there is no product that im asking for in this database.
Thanks
Try a FULL OUTER JOIN. You're using a LEFT JOIN. That requires that the DB fetch all records that match your WHERE clause on the LEFT side of the join, which is warehouse1, and any potentially matching records from warehouse2 (the right side of the join). If a SKU exists only in warehouse2, you don't see it.
Switching to a FULL OUTER JOIN forces the DB to fetch all matching records from BOTH sides of the join, regardless of which side(s) the matching records exist on.
you can also do this with a union
(SELECT
warehouse1.product.id as 1_id,
warehouse1.product.SKU,
warehouse1.product.qty as 1_qty
FROM `warehouse1`.`product`
WHERE
warehouse1.product.SKU = '$sku' )
union
(SELECT
warehouse2.product.id as 2_id ,
warehouse2.product.SKU,
warehouse2.product.qty as 2_qty
FROM `warehouse2`.`product`
WHERE warehouse2.product.SKU = '$sku' )
Combine your OR's in () (... OR ...):
SELECT
warehouse1.product.id as 1_id,
warehouse2.product.id as 2_id ,
warehouse1.product.SKU,
warehouse1.product.qty as 1_qty,
warehouse2.product.qty as 2_qty
FROM `warehouse1`.`product`
LEFT JOIN `warehouse2`.`product`
ON
(`warehouse1`.`product`.`SKU` = `warehouse2`.`product`.`SKU`)
WHERE (warehouse1.product.SKU = '$sku'
OR
warehouse2.product.SKU = '$sku')
ORDER BY
(1_qty + 2_qty) DESC

Categories