SQL/PHP: SELECT only one row per item - php

I have a table containing persons information (one row per person) and another table containing persons photos filenames (many rows per person). I want to select a group of persons (based on another table) but only one photo per person.
My old SQL was like this:
SELECT persons.personID, persons.name, persons.photo_filename, movie_cast.role
FROM persons, movie_cast
WHERE persons.personID = movie_cast.personID
AND movie_cast.imdbID = ?
ORDER BY movie_cast.castORDER
LIMIT 9';
But here, the 'persons' table contains also a 'photo_filename' column. In my new database design, this column is in another table. So if I try to get the photo_filename from the new table I get all the photos available for each person, but I need to get only one.
How to do it?

In the first example I have assumed there is always a photo, and am just grabbing the highest sorted photo filename alphabetically as a means to get a consistent photo for each user each time you run the query.
SELECT p.personID, p.name, ph.photo_filename, mc.role
FROM persons p
INNER JOIN movie_cast mc ON p.personID = mc.personID
INNER JOIN (
select personID, max(photo_filename) as MaxPhotoName
from photos
group by personID
) phm on p.personID = phm.personID
INNER JOIN photos ph on phm.personID = ph.personID
and phm.MaxPhotoName = ph.photo_filename
WHERE mc.imdbID = ?
ORDER BY mc.cast
LIMIT 9
If there is a photo_date column and you want to use the newest photo you can do it like this:
SELECT p.personID, p.name, ph.photo_filename, mc.role
FROM persons p
INNER JOIN movie_cast mc ON p.personID = mc.personID
INNER JOIN (
select personID, max(photo_date) as MaxPhotoDate
from photos
group by personID
) phm on p.personID = phm.personID
INNER JOIN photos ph on phm.personID = ph.personID
and phm.MaxPhotoDate = ph.photo_date
WHERE mc.imdbID = ?
ORDER BY mc.cast
LIMIT 9
If there is not always a photo, you can use a LEFT OUTER JOIN so that you will still get all your records back:
SELECT p.personID, p.name, ph.photo_filename, mc.role
FROM persons p
INNER JOIN movie_cast mc ON p.personID = mc.personID
LEFT OUTER JOIN (
select personID, max(photo_date) as MaxPhotoDate
from photos
group by personID
) phm on p.personID = phm.personID
LEFT OUTER JOIN photos ph on phm.personID = ph.personID
and phm.MaxPhotoDate = ph.photo_date
WHERE mc.imdbID = ?
ORDER BY mc.cast
LIMIT 9

Related

how to left join the relation to join five latest rows only

i am joining the latest relation row with the following code.
SELECT pos.* FROM users AS usr LEFT JOIN posts AS pos ON pos.user_id = usr.id WHERE
pos.id = (
SELECT MAX(id)
FROM posts
WHERE user_id = usr.id
);
if I want to join the second last row only here is the code
SELECT pos.* FROM users AS usr LEFT JOIN posts AS pos ON pos.user_id = usr.id WHERE
pos.id = (
SELECT id
FROM posts
WHERE user_id = usr.id
ORDER BY id DESC LIMIT 1,1
);
The question is what i can do if I want to join the latest five rows again in each relation?

Limit values for the left Join part of a query

Please i have four tables joined together using the LEFT JOIN, the images table is linked to the items table by img_item, thus each item can have more images. i want to fetch only the first image of every item. How do i go achieve this.
SELECT * FROM items
LEFT JOIN category ON items.item_cat = category.cat_id
LEFT JOIN users ON users.user_id=items.item_user
LEFT JOIN institutions ON institutions.inst_id=users.user_inst
LEFT JOIN images ON images.img_item = items.item_id
ORDER BY item_id DESC
In MySQL, you can enumerate the results using variables, and then choose the first. Another alternative is to identify which one you want, and choose that one. The following chooses the image with the largest id:
SELECT *
FROM items LEFT JOIN
category
ON items.item_cat = category.cat_id LEFT JOIN
users
ON users.user_id=items.item_user LEFT JOIN
institutions
ON institutions.inst_id = users.user_inst LEFT JOIN
images
ON images.img_item = items.item_id AND
images.img_id = (SELECT MAX(i2.img_id)
FROM images i2
WHERE i2.img_item = images.img_item
);
ORDER BY item_id DESC

Select X that has both Y and Z (SQL)

Ok, I have three tables, Colors,People,Likes
Colors contains ids and names colors, People contains ids and names of people, Likes contains color_id and people_id to describe which people like which colors.
Now, given a list of colors, how can I select every person (if any) who likes every color in the list?
select p.id, p.name
from people p
join likes l on l.people_id = p.id
join colors c on l.color_id = c.id
where c.name in ('blue','green','red')
group by p.id, p.name
having count(distinct c.name) = 3
You can GROUP BY the person joined on the Likes, and retrieve those tuples HAVING a COUNT(*) equal to the number of rows in the Color list. This way you
only need to join two tables
don't need to explicitly name the colors.
SELECT People.id, COUNT(*) AS ColorsLiked
FROM People JOIN Likes ON (People.id = Likes.people_id)
GROUP BY People.id
HAVING ColorsLiked = (SELECT COUNT(*) FROM Colors);
SELECT * FROM People p
INNER JOIN Likes l ON l.people_id = p.people_id
INNER JOIN Colors c ON l.color_id = c.color_id
WHERE c.name IN ('List of colors')

How do I calculate avg and count on multiple columns involving many tables?

Here are my different tables:
computers (id,name)
monitors (id,name)
computer_monitor (id, computer_id,monitor_id)
useractivity (id,userid,timestamp,computer_monitor_id,ip)
useropinion (id,userid,computer_monitor_id,timestamp,rating)
user (id,name,email)
I want to search after the name of computer or monitor and get a row like this in return:
computer name and/or monitor name
computer_monitor_id
avg(rate)
count(useractivity)
avg(rate) is on that specific computer_monitor_id that matches the name, the same goes for count.
A computer with no connection to monitor has a value of 0 on monitor field in computer_monitor table and vice versa for monitor->computer.
useractivity and useropinion only contains the ID from computer_monitor table
As I understand, the query should be built around the computer_monitor table. All other tables connect to it, including those from which you want to obtain the stats.
SELECT
c.name AS ComputerName,
m.name AS MonitorName,
uo.AverageRating,
ua.ActivityCount
FROM computer_monitor cm
LEFT JOIN computer c ON c.id = cm.computer
LEFT JOIN monitor m ON m.id = cm.monitor
INNER JOIN (
SELECT computer_monitor_id, AVG(rating) AS AverageRating
FROM useropinion
GROUP BY computer_monitor_id
) uo ON cm.id = uo.computer_monitor_id
INNER JOIN (
SELECT computer_monitor_id, COUNT(*) AS ActivityCount
FROM useractivity
GROUP BY computer_monitor_id
) ua ON cm.id = ua.computer_monitor_id
Actually, as you can see, useropinion and useractivity are aggregated first, then joined. This is to avoid the Cartesian product effect when a computer_monitor.id matches more than one row both in useropinion and in useractivity.
<?php
$res_comp = mysql_query("select * from computers where name = '$name'");
$res_monitor = mysql_query("select * from monitor where name = '$name'");
if(mysql_num_rows($res_comp) > 0)
{
$row_comp = mysql_fetch_array($res_comp);
$comp_id = $row_comp['id'];
$res_result = mysql_query("select computers.name, computer_monitor.id, count(computer_monitor_id) from computers, computer_monitor, useractivity where computers.id = '$comp_id' AND computer_monitor_id = '$comp_id' AND useractivity.computer_monitor_id = '$comp_id'");
}
// repeat the same for monitor also. then use mysql_fetch_array to show your data.
?>
hopefully this will help.
This might do the trick...(one table with the computer/monitor relation ship, the other with a xref table threw me, and check the join types depending on your data)
SELECT computers.name AS ComputerName
, monitors.name AS MonitorName
, AVG(useropinion.rating) AS AvgRating
, COUNT(useractivity.id) AS ActivityCount
FROM computers
INNER JOIN computer_monitor ON (computers.id = computer_monitor.computer_id)
INNER JOIN useractivity ON (computers.id = useractivity.computer_id)
INNER JOIN monitors ON (computer_monitor.monitor_id = monitors.id)
INNER JOIN useropinion ON (computer_monitor.id = useropinion.computer_monitor_id) AND (monitors.id = useractivity.monitor_id)
INNER JOIN USER ON (useropinion.user_id = user.id) AND (useractivity.user_id = user.id)

Counting rows from table after joins

I have a query that selects data from 4 tables via joins, i want to also count the number rows in a fifth table containing a matching foreign key.
This is what my current query looks look like, and it doesnt work
"SELECT
ph.pheed_id,ph.user_id,ph.datetime,ph.repheeds,
ph.pheed,fav.id,fav.P_id,fav.datetime as stamp,
u.username,ava.avatar_small
COUNT(pheed_comments.comment_id) as comments
FROM favourite_pheeds fav
INNER JOIN pheeds ph ON ph.pheed_id=fav.P_id
INNER JOIN users u ON u.id=ph.user_id
INNER JOIN profiles pr ON pr.user_id=ph.user_id
LEFT JOIN user_avatars ava ON ava.avatar_id=pr.avatar
ORDER BY stamp DESC
LIMIT $offset,$limit";
How do i count the number rows in a fifth table containing a matching foreign key.
select ph.pheed_id,
ph.user_id,
ph.datetime,
ph.repheeds,
ph.pheed,
fav.id,
fav.P_id,
fav.datetime as stamp,
u.username,
ava.avatar_small,
coalesce(pcc.Count, 0) as comments_count
from favourite_pheeds fav
inner join pheeds ph on ph.pheed_id = fav.P_id
inner join users u on u.id = ph.user_id
inner join profiles pr on pr.user_id = ph.user_id
left join user_avatars ava on ava.avatar_id = pr.avatar
left outer join (
select pheed_id, count(*) as Count
from pheed_comments
group by pheed_id --took a guess at the column name here
) pcc on ph.pheed_id = pcc.pheed_id
order by stamp desc
LIMIT $offset, $limit

Categories