Get a picture for each album - php

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.

Related

How do I SELECT and COUNT from two diferent tables to get the "like count" of a post in one query?

I'm kind of noobie to this, but I'm trying to learn, I have two tables, the first one (NEWS) has all the information about posts of a blog, it has the follow structure:
* NEWS (TABLE 1)
- id_new
- id_category
- date
- ...etc
- **likes**
and I have a second table:
* LIKES (TABLE 2)
- id_like
- id_new
- id_user
- date
- ip_user
So, I want to select all the rows from TABLE 1 to display all the news but also i want to count the likes and get the COUNT of each new as like column.
This approach left joins the NEWS table to a subquery which finds the number of likes for each news story.
SELECT
t1.*,
COALESCE(t2.likes, 0) AS likes
FROM NEWS t1
LEFT JOIN
(
SELECT id_new, COUNT(*) AS likes
FROM LIKES
GROUP BY id_new
) t2
ON t1.id_new = t2.id_new
Note that here a story having no likes would not appear at all in the LIKES table and would receive a count of zero. Also note that I assume that every record in the LIKES table corresponds to a logical like. If not, then the query could be modified to count something else.
You can do it like this
SELECT table1.*, table2.*, count(table2.id_like) as like FROM news AS table1
INNER JOIN likes AS table 2 ON table1.id_new = table2.id_new;
OR
SELECT table1.*, table2.*, count(table2.id_like) as like FROM news AS table1
LEFT JOIN likes AS table 2 ON table1.id_new = table2.id_new;
you can use prepared statement
for example
$stmt = $pdo->prepare("SELECT count(*) FROM TABLE_1);
$stmt2 = $pdo->prepare("SELECT count(*) FROM TABLE_2);
//then execute
just read more on prepared statement
Try this
SELECT n, (SELECT count(*) FROM like l WHERE l.id_new = n.id_new) FROM news n
Use something like :
SELECT *, (SELECT COUNT(*) FROM LIKES WHERE LIKES.id_new =id_new) AS newsLikesCount FROM NEWS ORDER BY date;
This query would return all news and their number of likes
select n1.* , numberOfLikes.number_of_likes
from news n1
left join
(select n.id_news, count(l.id_like) as number_of_likes
from news n
left join likes l on n.id_news = l.id_new
group by n.id_news) numberOfLikes on n1.id_news = numberOfLikes.id_news
The important concepts here is understanding how two tables are joined together (1), how group by works(2), and how to aggregate l.id_likes using count(3).
(1). Left join preserves everything in the NEWS table and join them
with news link to the news.
(2). Then we group the rows base on id_news from the news. However,
mysql gets confused because it doesn't know what to do with id_like
from the likes table that we included in our select clause. Don't
worry my friend, This is where count comes in.
(3). We count the number of id_likes base for each id_news since we
are grouping the rows base on id_news.
I hope this helps. and welcome to StackOverfow. If you find this answer of any other answer helpful please mark it as the solution. That way it will help the community and fellow programmers in the future if they run into the same problem as you. Cheers.
Edit: to include all columns from news table we simply join the result from above back to the news table itself. and we select everything from the news table n1 and only number_of_likes from the result we created above.

How do you control which match is chosen when running a `SELECT INNER JOIN WHERE t1.column=t2.column` and there are multiple matches?

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

Math between two tables with MySQL and PHP

I have two tables in my SQL database, one for the photos that users sent and another one for the votes which photo had on my application. I need to extract the 30 photos from the 'photos' table which had the most votes on the 'votes' table.
Is there a way to do it within a single query?
You should be able to use a query like this:
select
a.photoFileName
from
photos a
join votes b
on a.photoId=b.photoId
order by
b.voteCount desc
limit 30
Adjust the keys to your exact column names on the linked fields.
This assumes that the votes table has an number column (voteCount) that has a tally of the votes for that image.
Something like this ( if each vote is stored single ), but make your own adjustments:
SELECT
p.id,
COUNT( v.id )
FROM
photos p
JOIN
votes v ON p.id = v.photo_id
ORDER BY
COUNT( v.id ) DESC
GROUP BY
v.photo_id
LIMIT 30;
PS: I did not test the query, just gave you an example!

Complicated MySQL Database Query

I have the following database structure:
Sites table
id | name | other_fields
Backups table
id | site_id | initiated_on(unix timestamp) | size(float) | status
So Backups table have a Many to One relationship with Sites table connected via site_id
And I would like to output the data in the following format
name | Latest initiated_on | status of the latest initiated_on row
And I have the following SQL query
SELECT *, `sites`.`id` as sid, SUM(`backups`.`size`) AS size
FROM (`sites`)
LEFT JOIN `backups` ON `sites`.`id` = `backups`.`site_id`
WHERE `sites`.`id` = '1'
GROUP BY `sites`.`id`
ORDER BY `backups`.`initiated_on` desc
The thing is, with the above query I can achieve what I am looking for, but the only problem is I don't get the latest initiated_on values.
So if I had 3 rows in backups with site_id=1, the query does not pick out the row with the highest value in initiated_on. It just picks out any row.
Please help, and
thanks in advance.
You should try:
SELECT sites.name, FROM_UNIXTIME(b.latest) as latest, b.size, b.status
FROM sites
LEFT JOIN
( SELECT bg.site_id, bg.latest, bg.sizesum AS size, bu.status
FROM
( SELECT site_id, MAX(initiated_on) as latest, SUM(size) as sizesum
FROM backups
GROUP BY site_id ) bg
JOIN backups bu
ON bu.initiated_on = bg.latest AND bu.site_id = bg.site_id
) b
ON sites.id = b.site_id
In the GROUP BY subquery - bg here, the only columns you can use for SELECT are columns that are either aggregated by a function or listed in the GROUP BY part.
http://dev.mysql.com/doc/refman/5.5/en/group-by-hidden-columns.html
Once you have all the aggregate values you need to join the result again to backups to find other values for the row with latest timestamp - b.
Finally join the result to the sites table to get names - or left join if you want to list all sites, even without a backup.
Try with this:
select S.name, B.initiated_on, B.status
from sites as S left join backups as B on S.id = B.site_id
where B.initiated_on =
(select max(initiated_on)
from backups
where site_id = S.id)
To get the latest time, you need to make a subquery like this:
SELECT sites.id as sid,
SUM(backups.size) AS size
latest.time AS latesttime
FROM sites AS sites
LEFT JOIN (SELECT site_id,
MAX(initiated_on) AS time
FROM backups
GROUP BY site_id) AS latest
ON latest.site_id = sites.id
LEFT JOIN backups
ON sites.id = backups.site_id
WHERE sites.id = 1
GROUP BY sites.id
ORDER BY backups.initiated_on desc
I have removed the SELECT * as this will only work using MySQL and is generally bad practice anyway. Non-MySQL RDBSs will throw an error if you include the other fields, even individually and you will need to make this query itself into a subquery and then do an INNER JOIN to the sites table to get the rest of the fields. This is because they will be trying to add all of them into the GROUP BY statement and this fails (or is at least very slow) if you have long text fields.

How to use join for more than two table?

I have three tables.They are
tb_albums---->id,title, description
tb_photos---->id,album_id, photo
tb_tags---->id,album_id, tag
Here i want to get the albums details and it photos & it tags through tb_albums.id.
How to use join query here?
Normally, you can use as many tables in JOIN, as you'd like. Just add another JOIN statement. You can refer to k102's answer for the correct syntax (which doesn't produce correct result though).
But in this particular case you don't want to use simple JOIN on all tables, unless you have only one photo per album and only one tag per album. If you have more than one photo per and more than one tag per album, JOIN both tables on album_id in single query will produce Cartesian product of both, in other words all possible combinations of tags and photos from each albums. For N photos and M tags that's N * M results, instead of N + M.
Also, there is no point of joining with tb_albums, as you do not need to repeat information about each album for each photo and each tag.
Proper approach would be to have 3 separate simple SELECTs from each table and combining their result on application level.
If for some awkward reason you'd need to do that with one query, you can do something like:
SELECT * FROM tb_albums as A JOIN
(SELECT 'photo', id, photo as value FROM tb_photos
UNION ALL
SELECT 'tag', id, tag as value FROM tb_tags) as B ON B.album_id = A.id
Note, this is way less optimal than separate SELECTs, you should only do this if you have no other choice.
select * from tb_albums a
join tb_photos p on a.id = p.album_id
join tb_tags t on a.id = t.album_id

Categories