I have 2 tables, one called "products" and one "images".
The table "images" hold the images of each products, so I can have 5 image per product.
I want to make a select that retrive only 1 image for each product. I'm new to joins so i dont know how to solve this.
I'm trying with:
SELECT *
FROM products
INNER JOIN images ON products.id=images.prod_id
WHERE products.cat='shoes'
I need to add a Limit 0,1 on images table. How I can do it?
Thanks in advance.
Maybe a subselect is a better solution here.
Something like this:
SELECT
productId,
productName,
(SELECT imageData FROM Image i WHERE i.productId = productId LIMIT 1) AS imageData
FROM Products
It's best to avoid subqueries because they are slow in mysql.
If you want to get any image associated to product,
you can do it in fast but not very nice way:
SELECT *
FROM products
INNER JOIN images ON products.id=images.prod_id
WHERE products.cat='shoes'
GROUP BY products.id
If you want to get a first image( by any criteria ), apply groupwise max techniques
Take a look at DISTINCT
The key here is correlated subqueries.
select
*
from
products p,
(
select
*
from
images i
where
i.prod_id = p.id
limit 1
) as correlated
where
p.cat = 'shoes'
SELECT * FROM products
LEFT JOIN images ON products.id=images.prod_id
WHERE products.id='1' LIMIT 1
This will return the first image found for your product and all the product details.
If you want to retieve multiple products then I would suggest doing 2 queries.
SELECT product data
Loop through product data {
SELECT image data LIMIT 1
}
Doing complex single queries can quite often end up being more expensive than a couple/few smaller queries.
Related
I have two MySQL tables:
Products
title id hidden categories
Thing 10 N 12,14,
Stuff 23 N 12,
Object 41 Y 13,14
Images
filename id productid
aca8t.jpg 1 10
ev7ha.jpg 2 10
mscpk.jpg 3 10
asges.jpg 4 23
fcuhg.jpg 5 23
scvfe.jpg 6 41
vf6kl.jpg 7 41
fgszy.jpg 8 41
I build a list of product titles and images using a SELECT statement like this:
SELECT t1.title,
t1.id,
t1.hidden,
t1.categories,
t2.image
FROM products AS t1
INNER JOIN pimage AS t2
WHERE t1.categories LIKE '%$categoryId%'
AND t1.id=t2.productid
AND NOT t1.hidden='Y'
GROUP BY id
ORDER BY id ASC
After running this query, I have a list of non-hidden products in a given category, as well as their IDs and one image. However, the selection of an image appears to be random. Sometimes it's alphabetical, sometimes it's the lowest ID, and sometimes it's neither. However, it's always the same when the filenames for a given productid stay the same.
This is in use on a small website where product managers upload photos of a product and its accessories. The first photo should be used as a thumbnail and visible on the category page. However, a photo of a random accessory is sometimes selected as the thumbnail image, and the product managers have to re-upload the images until the right one gets selected. This process is onerous.
How can I modify the SQL statement so that the first photo (the filename with the lowest images.id) is selected?
Try using a correlated subquery instead of a join/group by:
SELECT p.*,
(SELECT i.image
FROM pimage i
WHERE p.id = i.productid
ORDER BY i.image ASC
LIMIT 1
) as image
FROM products p
WHERE p.categories LIKE '%$categoryId%' AND
p.hidden <> 'Y'
ORDER BY p.id ASC
If this statement were executed on another relational database (other than MySQL/MariaDB), it would throw an error with a message along the lines of "non-aggregates in SELECT list not included in GROUP BY".
But A MySQL specific extension to GROUP BY allows this query to execute in MySQL, but as you've noticed, the values returned for the non-aggregates in the SELECT list are indeterminate, MySQL will return a value from some row.
The normal pattern is to use a MAX() or MIN() aggregate function to "control" which value is returned.
In your case, that would work to return the minimum id value, but getting the other values on that same row is more problematic. If you only need to return a few columns, you can use a correlated subqueries in the SELECT list.
Another approach is to use an inline view and a join operation.
SELECT t1.title
, t1.id
, t1.hidden
, t1.categories
, t2.image
FROM products t1
JOIN ( SELECT n.productid
, MIN(n.id) AS min_id
FROM pimage n
GROUP BY n.productid
) m
ON m.productid = t1.id
JOIN pimage t2
ON t2.id = m.min_id
WHERE t1.categories LIKE '%$categoryId%'
AND t1.hidden='Y'
GROUP BY t1.id
ORDER BY t1.id ASC
This approach is useful when you need to return a additional columns from the row with the "minimum" id. For example, you also needed to include in the SELECT list:
, t2.fee
, t2.fi
, t2.fo
, t2.fum
I have a question about performance, I guess that I am doing something wrong.
I have one table with Items, and another one with Categories, any item can be in multiple categories. Now I want to show all Items with are not included in any category.
Connections are in table Items_Categories (just with ID_item and ID_category)
I made this query:
SELECT *
FROM Items AS i
WHERE ID_item NOT
IN (
SELECT DISTINCT ID_item
FROM Items_Categories
)
It works, but it's very slow. It takes a few seconds, and my DB is not so big. I have about 3000 Items and maybe 200 categories.
Any better solutions?
You can probably use a JOIN instead and find the ones that don't have any matching.
SELECT i.*
FROM items i
LEFT JOIN Items_Categories ic
ON i.ID_item = ic.ID_Item
WHERE
ic.ID_Item IS NULL
Try this way:
SELECT i.*
FROM Items AS i
LEFT JOIN Items_Categories IC on IC.ID_item = I.ID_item
where IC.ID_item is null
You could take this approach and check the execution plan of yours and mine with EXPLAIN, as Mark Baker recommended:
SELECT
*
FROM
Items i
WHERE NOT EXISTS (
SELECT 1 FROM Items_Categories WHERE ID_item = i.ID_item
);
You should have indexed the ID_item column in both tables of course.
I've been racking my brain for hours trying work out how to join these two queries..
My goal is to return multiple venue rows (from venues) based on certain criteria... which is what my current query does....
SELECT venues.id AS ven_id,
venues.venue_name,
venues.sub_category_id,
venues.score,
venues.lat,
venues.lng,
venues.short_description,
sub_categories.id,
sub_categories.sub_cat_name,
sub_categories.category_id,
categories.id,
categories.category_name,
((ACOS( SIN(51.44*PI()/180)*SIN(lat*PI()/180) + COS(51.44*PI()/180)*COS(lat*PI()/180)*COS((-2.60796 - lng)*PI()/180)) * 180/PI())*60 * 1.1515) AS dist
FROM venues,
sub_categories,
categories
WHERE
venues.sub_category_id = sub_categories.id
AND sub_categories.category_id = categories.id
HAVING
dist < 5
ORDER BY score DESC
LIMIT 0, 100
However, I need to include another field in this query (thumbnail), which comes from another table (venue_images). The idea is to extract one image row based on which venue it's related to and it's order. Only one image needs to be extracted however. So LIMIT 1.
I basically need to insert this query:
SELECT
venue_images.thumb_image_filename,
venue_images.image_venue_id,
venue_images.image_order
FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1
Into my first query, and label this new field as "thumbnail".
Any help would really be appreciated. Thanks!
First of all, you could write the first query using INNER JOIN:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
HAVING
...
the result should be identical, but i like this one more.
What I'd like to do next is to JOIN a subquery, something like this:
...
INNER JOIN (SELECT ... FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1) first_image
but unfortunately this subquery can't see ven_id because it is evaluated first, before the outer query (I think it's a limitation of MySql), so we can't use that and we have to find another solution. And since you are using LIMIT 1, it's not easy to rewrite the condition you need using just JOINS.
It would be easier if MySql provided a FIRST() aggregate function, but since it doesn't, we have to simulate it, see for example this question: How to fetch the first and last record of a grouped record in a MySQL query with aggregate functions?
So using this trick, you can write a query that extracts first image_id for every image_venue_id:
SELECT
image_venue_id,
SUBSTRING_INDEX(
GROUP_CONCAT(image_id order by venue_images.image_order),',',1) as first_image_id
FROM venue_images
GROUP BY image_venue_id
and this query could be integrated in your query above:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
INNER JOIN (the query above) first_image on first_image.image_venue_id = venues.id
INNER JOIN venue_images on first_image.first_image_id = venue_images.image_id
HAVING
...
I also added one more JOIN, to join the first image id with the actual image. I couldn't check your query but the idea is to procede like this.
Since the query is now becoming more complicated and difficult to mantain, i think it would be better to create a view that extracts the first image for every venue, and then join just the view in your query. This is just an idea. Let me know if it works or if you need any help!
I'm not too sure about your data but a JOIN with the thumbnails table and a group by on your large query would probably work.
GROUP BY venues.id
I have two table for gallery system :
gallery_cat(
gallery_cat_id PK,
gallery_cat_name
)
gallery(
gallery_id PK,
gallery_cat_id FK,
gallery_name,
gallery_file_name,
gallery_date
)
I need to write a SQL query that return one picture from gallery table for each album, the purpose of this that I need to list the albums with one picture for each.
gallery_name | gallery_cat_name| gallery_file_name
-------------+-----------------+------------------
pic1 | Album1 | pic1.jpg
This should do the trick:
SELECT g2.gallery_name, gc2.gallery_cat_name, g2.gallery_file_name
FROM gallery g2
INNER JOIN gallery_cat gc2 ON (g2.gallery_cat_id = gc2.gallery_cat_id)
WHERE g2.gallery_id IN (
SELECT g.gallery_id
FROM gallery g
GROUP BY g.gallery_cat_id)
Explanation:
At the end is a sub-select
IN (
SELECT g.gallery_id
FROM gallery g
GROUP BY g.gallery_cat_id) <<-- select 1 random g.id per gallery_cat.
Here I select all g.id, but because of the group by clause it will reduce the results to 1 row per grouped by item. I.e. 1 row (chosen more or less at random) per g.gallery_cat_id.
Next I do a normal select with a join:
SELECT g2.gallery_name, gc2.gallery_cat_name, g2.gallery_file_name
FROM gallery g2
INNER JOIN gallery_cat gc2 ON (g2.gallery_cat_id = gc2.gallery_cat_id)
WHERE g2.gallery_id IN (
Because I refer to the same table twice in the same query you have to use an alias(*).
I select all names and all catnames and all filenames.
However in the where clause I filter these so that only rows from the sub-select are shown.
I have to do it this way, because the group by mixes rows into one messed up ow, if I select from that directly I will get values from different rows mixed together, not a good thing.
By first selecting the id's I want and then matching full rows to those id I prevent this from happening.
*(in this case with this kind of subselect that's not really 100% true, but trust me on the point that it's always a good idea to alias your tables)
This attempts to select the most recent gallery_date for each category ID and join against gallery_cat
SELECT
c.gallery_cat_id,
c.gallery_cat_name,
i.lastimg
FROM
gallery_cat c
LEFT JOIN (
SELECT gallery_cat_id, gallery_filename AS lastimg, MAX(gallery_date)
FROM gallery
GROUP BY gallery_cat_id, gallery_filename
) i ON c.gallery_cat_id = i.gallery_cat_id
You can use SQL JOINS to do this, otherwise you would have to loop out all the albums and pick one random picture from each which would be less efficient.
this site is like an ecommerce site and i have two tables that i'm stuck on.
tbl_products
---------------
phash
product
price
desc
maxcount
tbl_product_images
-------------------
phash
thumb
large
tbl_products stores the products description
tbl_product_images stores the path of the thumb and large image of eac product.
phash is an md5 of the products name its what i use rather than the id or product name when matching things together.
what i'm having throuble with is say tbl_products has one record for a product and tbl_product_images
in relation to that product has 5 rows for its images.
how would i run the query?
$sql = "select
tbl_products.phash
tbl_products.product
tbl_products.price
tbl_products.desc
tbl_products.maxcount
tbl_product_images.phash
tbl_product_images.thumb
tbl_product_images.large
from tbl_products
inner join tbl_product_images
on tbl_products.phash = tbl_products_images.phash";
this will display 5 rows of records since tbl_product_images has 5 records.
the way i output is my traditional way
$query = $db->query("$sql");
while($row = $db->fetch($query))
{
....
}
i am really unsure how i'm going to do this, if you need more explanation to what i'm trying to explain please let me know. thanks
The simplest way is to limit the number of results coming from the images table - though that's only appropriate if you don't care which image is returned for each product:
select
tbl_products.phash
tbl_products.product
tbl_products.price
tbl_products.desc
tbl_products.maxcount
tbl_product_images.phash
tbl_product_images.thumb
tbl_product_images.large
from tbl_products
inner join (SELECT * FROM tbl_product_images LIMIT 1) images
on tbl_products.phash = images.phash
You can add an ORDER BY [col] DESC to that LIMITed query if there's some other column in the images table (like a date) you could use to determine which image appears.
(Note: If there were some column in tbl_product_images that determined which image to return, you could either put that in a WHERE clause on the query, or include it in the join criteria, like: ON tbl_products.phash = tbl_product_images.phash AND tbl_product_images.color = 'Blue')
Further, if you are trying to get a product with all of its images, but only want one row in the resultset, it may be possible to "pivot" the set of images into columns. However, this is tricky if you can have an arbitrary (or simply large) number of images per product. You may be better off just returning multiple rows per product, and handling the results in your application code...