The website contains images. These images can be ranked. When an image is ranked, the value can be 1,2, or 3. To save ranking I have a table ranking_items. The images are displayed as thumbnails. The boss would like me to order them by rank. the problem is, how do I also include images in the result with no entry in the ranking_items?
$db->query("SELECT file_name
FROM images, ranking_items
WHERE images.id=ranking_items.image_id
ORDER BY ranking_items.rank ASC");
When you do FROM table1, table2 you are doing a JOIN.
Try to LEFT JOIN the ranking_items table. This will return all rows, and put NULLS in the places where the join fails.
SELECT file_name
FROM images
LEFT JOIN ranking_items ON images.id=ranking_items.image_id
ORDER BY ranking_items.rank ASC
SELECT file_name
FROM images LEFT JOIN ranking_items
ON images.id=ranking_items.image_id
ORDER BY ranking_items.rank ASC
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 an images table which stores an image id, title, the user who uploads it, a timestamp and file extension.
I also have a views table which records when an image has been viewed. the columns are id, image id, timestamp and the user who viewed the image.
What i need and have no idea how to do is to run an SQL query which will return a set of images in order of the most viewed.
I've been looking around on here and Google but just don't know what to actually search.
SELECT images.*, count(views.view_id) view_count
FROM images
LEFT JOIN views on views.image_id = images.image_id
GROUP BY images.image_id
ORDER BY view_count DESC
http://sqlfiddle.com/#!2/a669b/5
The GROUP BY clause tells the "aggregate functions" (count, sum, min, max, etc) how to behave. In this case, we are grouping by image and grabbing the count of views per image. COUNT only count non-null values, and we don't want to count the views that don't exist, so we're keying off views.view_id.
If you do an INNER JOIN rather than a LEFT JOIN, it will only return the images which have views. The LEFT JOIN allows us to get counts of zero.
You need to be looking at joins, join the images table to the images viewed table, then you can run one query across both tables.
I have one image table and image view status table.
In each click of image I am tracking image view time. status. date time.. So in image view status table have more than one entry for one image.
I need to check whether one user already viewed one image or not.
So I write one query like this..
select A.title, if(B.view_status='completed','completed','notviewed') from images A left join imgstatus_view B on (A.imgId=B.ImgId and A.fileId=B.fileID) Where userId=1 and imgId=121
If I view one image three times its fetching 3 records. I need only one record to check whether user viewed the image or not.
So with out using DISTINCT or GROUP BY How can I fetch unique row of data.
Please help me.
you can use ORDER BY and LIMIT clause. to get most recent record you can use below query
select A.title, if(B.view_status='completed','completed','notviewed') from images A left join imgstatus_view B on (A.imgId=B.ImgId and A.fileId=B.fileID) Where userId=1 and imgId=121 ORDER BY datetimefield DESC LIMIT 0,1
to get old record
select A.title, if(B.view_status='completed','completed','notviewed') from images A left join imgstatus_view B on (A.imgId=B.ImgId and A.fileId=B.fileID) Where userId=1 and imgId=121 ORDER BY datetimefield LIMIT 0,1
if you dont have datetime field remove ORDER BY clause which will give most recent modified record
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.
I basically have three tables, posts, images and postimages (this simply contains the ids of both the other tables and allows more than one image in each post and each image to be reused in multiple posts if necessary).
I'm trying to list the titles of all the posts, along with a single, small image from each (if one exists)
I can do it with the following:
$sql ="SELECT * FROM posts p, images im, postimages pi WHERE
AND pi.post_id = p.id
AND pi.image_id = im.image_id
ORDER BY created_at LIMIT 10";
But obviously, if there is more than one image, the record is displayed twice.
I know I could probably do it with a subquery, but that seems horribly inefficient. Is there a more elegant way around this?
Any advice would be appreciated. Thanks.
select
*
from
posts p
left outer join (select post_id, max(image_id) as image_id
from postimages group by post_id) pi on
p.id = pi.post_id
left outer join images im on
pi.image_id = im.image_id
You can do the subquery so that it only has to be executed once, not per row. This way, it's used as a subquery for an entire table, and then joined to the posts. Obviously, this can be MIN(image_id) if you want to take the first image. Really, whatever you prefer.
Edit: Made it be a left outer join to capture even those images that don't exist in the posts. This will return null for the image in the case that it happens to be non-existant.
SELECT p.id,p.title,im.src
FROM posts p
LEFT JOIN postimages pi
ON p.id = pi.post_id
LEFT JOIN images im
ON pi.image_id = im.image_id
GROUP BY p.id
ORDER BY created_at
LIMIT 10
Not sure if this is the most efficient, you will have to run this against a inner query.
It will work only for MySql as far as I know.