Select one record after order by - mysql

i have a bunch of CD's that contain a bunch of artwork. I want to display a thumb image of each CD on a webpage and for that i need to pick just one image out of the many artwork that exist for that CD...
The query selects all of the artwork and i group by position and want to pick the first one. I don't no how to pick just the first image FOR EACH RECORD after i've order them...
The query is as following:
SELECT release_artwork.release_id, release_artwork.position, artwork.small_filename, artwork.small_width, artwork.small_height
FROM release_artwork
JOIN artwork ON artwork.artwork_id = release_artwork.artwork_id
ORDER BY release_artwork.position

I don't have a MySQL instance handy, but this should work - just define an inline table to hold the minimums, then join to that.
SELECT release_artwork.release_id, release_artwork.position, artwork.small_filename, artwork.small_width, artwork.small_height
FROM
release_artwork
inner JOIN artwork ON artwork.artwork_id = release_artwork.artwork_id
inner join
(
SELECT
ra.release_id as release_id
,min(ra.position) as rap
FROM
release_artwork ra
group by
ra.release_id
) mins
on mins.release_id = release_artwork.release_id
and mins.rap = release_artwork.position
ORDER BY
release_artwork.release_id

In this case you can use LIMIT 1, to limit the results you retrieve to just the first one.

SELECT release_artwork.release_id, release_artwork.position, artwork.small_filename, artwork.small_width, artwork.small_height
FROM release_artwork
JOIN artwork ON artwork.artwork_id = release_artwork.artwork_id
ORDER BY release_artwork.position LIMIT 1
Adding the LIMIT 1 at the end will limit it to a single result.

Related

SQL: How to merge two complex queries into one, where the second one needs data from the first one

The goal is to load a list of chats where the user sending the request is a member in. Some of the chats are group chats (more than two members) and there I want to show the profile pictures from the users who wrote the last three messages.
The first query to load meta data like the title and the timestamp of the chat is:
SELECT Chat_Users.ID_Chat, Chats.title, Chats.lastMessageAt
FROM Chat_Users
JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) = 2
AND MAX(Chat_Users.ID_User = $userID) > 0
ORDER BY Chats.lastMessageAt DESC
LIMIT 20
The query to load the last three profile pictures from one of the chats loaded with the query above is:
SELECT GROUP_CONCAT(innerTable.profilePictures SEPARATOR ', ') AS 'ppUrls',
innerTable.ID_Chat
FROM
(
SELECT Chat_Users.ID_Chat, Users.profilePictureUrl AS profilePictures
FROM Users
JOIN Chat_Users ON Chat_Users.ID_User = Users.ID
JOIN Chat_Messages ON Chat_Messages.ID_Chat = Chat_Users.ID_Chat
WHERE Chat_Users.ID_Chat = $chatID
ORDER BY Chat_Messages.timestamp DESC
LIMIT 3
) innerTable
GROUP BY innerTable.ID_Chat
Both are working separately but I want to merge them together so I don't have to run the second query in a loop due to performance reasons. Unfortunately I have no idea how this can be achieved because the second query needs the $chatID, which it only gets from the first query.
So to clarify the desired result: The list with the profile picture urls (second query) should be just another column in the result of the first query.
I hope it is explained in a reasonably understandable way. Any help would be much appreciated.
Edit: Sample data from the affected tables:
Table "Chats":
Table "Chat_Users":
Table "Chat_Messages":
Table "Users":
This fufils the brief, however it requires a view because MySQL 5.x doesn't support the WITH clause.
It's long and cluncky and I've tried to shorten it but this is as good as I can get, hopefully someone will pop up in the comments with a way to make it shorter!
The view:
CREATE VIEW last_interaction AS
SELECT
id_chat,
id_user,
MAX(timestamp) AS timestamp
FROM chat_messages
GROUP BY id_user, id_chat
The query:
SELECT
Chat_Users.ID_Chat,
Chats.title,
Chats.lastMessageAt,
urls.pps AS profilePictureUrls
FROM Chat_Users
JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
JOIN (
SELECT
lo.id_chat,
GROUP_CONCAT(users.profilePictureUrl) AS pps
FROM last_interaction lo
JOIN users ON users.id = lo.id_user
WHERE (
SELECT COUNT(*) -- the amount of more recent interactions
FROM last_interaction li
WHERE (li.timestamp = lo.timestamp AND li.id_user > lo.id_user)
) < 3
GROUP BY id_chat
) urls ON urls.id_chat = Chats.id
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) > 2
AND MAX(Chat_Users.ID_User = $userID)
ORDER BY Chats.lastMessageAt DESC
LIMIT 20

mysql left join get two queries

ok i have two tables, houses and pictures. what i would like to call is
for each house i would like to call number of pictures for that house and also minimum sort_order number for that photo.
i tried left join in different types, one returns me total photos which is fine ,but doesnt return the min sort order, the other one returns me min sort order but doesnt give me total photos.
my current code is
SELECT properties.p_id, properties.s_property_id, properties.a_property_id,
properties.p_advert_heading, properties.p_postcode, properties.p_price,
properties.p_bedrooms, properties.p_bathrooms, properties.p_status,
properties.p_priority, property_photos.photo_url, property_photos.sort_order, property_photos.photo_local,
COUNT(property_photos.p_id) AS tot_photos
FROM properties
LEFT JOIN property_photos on properties.p_id = property_photos.p_id
WHERE properties.is_archieved = 0 AND properties.p_status = 1 AND properties.a_id = 16
GROUP BY properties.p_id
ie:
house_table
----------
property_id
property_name
photo_table
-----------
photo_id
property_id
sort_order
so i need to find total number of pictures and also the id of the photo with minimum sort_order...
You can get both values most easily by using the substring_index()/group_concat() method to get the first photo_id:
select property_id, count(*) as numphotos,
substring_index(group_concat(photo_id order by sort_order), ',', 1) as min_photo_id
from photo_table pt
group by property_id;
You just need to use GROUP BY and aggregation functions:
SELECT
h.property_id,
h.property_name,
COUNT(p.photo_id) AS photo_count,
MIN(p.sort_order) AS min_sort
FROM house_table AS h
LEFT JOIN photo_table AS p
ON h.property_id = p.property_id
GROUP BY h.property_id

Join tables with specific order

I've got two tables, for example: Teacher and Pupil and table LastViewedPupil with fields who watched him and when (teacherId & pupilId). So I want to return the list of Pupils that was ordered by last viewed date, but there are not all pupils inside LastViewedPupil, but last few for example, I want to show after that ordered by date all left records no matter in wich order, how can I do that?
I can do without last part like
select * from Pupil as p, (
select * from LastViewedPupil lvp where lvp.teacherId = 5 ORDER BY lastViewDate
) as lvp where lvp.pupilId = p.pupilId;
Or should I add corresponding records in LastViewDatePupil for all pupils or need to Join table itself (sounds awkward)?
You should try this one:
SELECT p.*
LEFT JOIN LastViewedPupil lvp ON p.id = lvp
WHERE lvp.teacher_id = 5
ORDER BY lvp.lastViewDate DESC
I'm not sure if that query puts NULL at the beginning or at the end. If that doesn't order the results properly, try this other. I used a CASE for reordering data
SELECT p.*,
CASE WHEN lvp.lvp.lastViewDate IS NULL THEN 1 ELSE 0, END AS notNullfirst FROM Pupil p
LEFT JOIN LastViewedPupil lvp ON p.id = lvp
WHERE lvp.teacher_id = 5
ORDER BY notNullfirst, lvp.lastViewDate DESC

Keeping returned records from mysql unique

Is there a quick way to make sure the records you return from a MySQL JOIN query are unique?
The code below could potentially bring back the same category twice. Its the category ID which should be distinct!
SELECT
exp_categories.cat_name, exp_categories.cat_id, exp_categories.cat_url_title
,exp_category_posts.entry_id, exp_channel_titles.status
FROM (exp_categories
LEFT JOIN exp_category_posts
ON exp_categories.cat_id = exp_category_posts.cat_id)
LEFT JOIN exp_channel_titles
ON exp_category_posts.entry_id = exp_channel_titles.entry_id
WHERE exp_categories.group_id = 2
AND exp_category_posts.entry_id IS NOT NULL
AND exp_channel_titles.status = 'open'
ORDER BY RAND()
LIMIT 2
If I understand what you need you could close your query with:
GROUP BY exp_categories.cat_id
ORDER BY RAND()
LIMIT 2
The problem here is that you're joining one-to-many, so you will see as many rows as there are records in the "many" table. If you only want to bring back one row per category, you need to either only query categories or decide what criteria you want to use to choose single values from exp_category_posts.
One example option is to query for the most recent post in a category and join on that resultset instead of exp_category_posts.
SELECT
exp_categories.cat_name, exp_categories.cat_id, exp_categories.cat_url_title
,exp_category_posts.entry_id, exp_channel_titles.status
FROM (exp_categories
LEFT JOIN exp_category_posts
ON exp_categories.cat_id = exp_category_posts.cat_id)
LEFT JOIN exp_channel_titles
ON exp_category_posts.entry_id = exp_channel_titles.entry_id
WHERE exp_categories.group_id = 2
AND exp_category_posts.entry_id IS NOT NULL
AND exp_channel_titles.status = 'open'
GROUP BY exp_categories.cat_id
ORDER BY RAND()
LIMIT 2
edit: Adding the column you want to be distinct in the GROUP BY clause will ensure there's only 1 returned. Also, the ORDER BY RAND() is heavily discouraged, see: http://www.titov.net/2005/09/21/do-not-use-order-by-rand-or-how-to-get-random-rows-from-table/

mysql limiting join

I've done a few searches on this subject but non of the solutions seem to work so perhaps my requirement is slightly different.
Basically I have a "content" table and a "file_screenshots" table. Each row in the "file_screenshots" table has a "screenshot_content_id" column. I want to select from the "content" table, join the "file_screenshots" table but only select a maximum of 5 screenshots for any single piece of content.
If this isn't possible i'm happy to use two queries, but again i'm not sure how to limit the results to only receiving 5 screenshots per piece of content.
Here is an example query:
SELECT * FROM content
LEFT JOIN file_screenshots
ON file_screenshots.screenshot_content_id = content.content_id
WHERE content_type_id = 4
Assuming you have some sort of unique id column in your file_screenshots table, this should work for you:
SELECT
c.*,
fs.*
FROM
content c
JOIN
file_screenshots fs
ON (fs.screenshot_content_id = c.content_id)
LEFT JOIN
file_screenshots fs2
ON (fs2.screenshot_content_id = c.content_id AND fs2.id < fs.id)
GROUP BY
fs.id
HAVING
COUNT(*) < 5
ORDER BY c.content_id, fs.id
I've named the id column id. Rename it if neccessary.
If you want the 5 screenshots with the highest id, reverse the fs2.id vs. fs.id comparison.
ON (fs2.screenshot_content_id = c.content_id AND fs2.id > fs.id)