How to get 1 row per user with INNER JOIN - php

I have two tables one is user and another is images. I gave users the option to select multiple images. I can store multiple images with same user_id in database, but when I am trying to get one image from every user I am getting all the images.
My query is something like this:
$query = "
SELECT *
FROM images i
JOIN users u
ON u.user_id = i.user_id
LIMIT 1";
When I run this query in while() loop, I only get very first image from images table.
I am really sorry if I am not able to clarify what I am try to ask.

Have you tried something like this:
SELECT * FROM users u INNER JOIN images i ON u.user_id = i.user_id GROUP BY u.user_id;
This should return you only one record from user/image tables for each user that has an image.
Don't run queries in a while loop. Instead, use one query to get all the desired records.
If you insist on running your query in a loop, then you are missing WHERE users.user_id = ? part in your query, so you can get a different result for each user in a loop.

you can do this without using join. simple select user and fetch data and on the bases of 'id' add query to get image. i hope this will help you;

Your current query:-
SELECT *
FROM images i
JOIN users u
ON u.user_id = i.user_id
LIMIT 1
uses LIMIT 1. This tells the query to bring back 1 row.
Removing the LIMIT 1 will return 1 or more records per user (who has at least 1 image), one for each image.
If you want a single user then it is possible (although not recommended) to (ab)use the GROUP BY clause:-
SELECT *
FROM images i
JOIN users u
ON u.user_id = i.user_id
GROUP BY u.user_id
This would bring back one record per user, but which image it returns is not defined. It could bring back the first image for that user, or the last one, or any other one (and which one it returns could change in the future). Further, there is no actual reason it couldn't return values from different rows for each of the columns on the images table (unlikely, but nothing specified to stop this happening).
Note that basic SQL standards specify (with a small exception) that any non aggregate field brought back in a SELECT statement must be in the GROUP BY clause. MySQL used to not enforce this restriction by default, but recently this changed and it is enforced by default. As such by default this query would no longer work as it is returning all the fields from the images and users tables while only specifying the user_id from the users table in the GROUP BY clause.
What you should do is define which image you want for each user. Assuming the first image (and that the images table uses an auto increment primary key called id):-
SELECT u.*,
i.*
FROM users u
LEFT OUTER JOIN
(
SELECT user_id
MIN(id) AS first_image_id
FROM images
GROUP BY user_id
) sub0
ON u.user_id = sub0.user_id
LEFT OUTER JOIN images i
ON sub0.user_id = i.user_id AND sub0.first_image_id = i.id
This uses a sub query to get the first image id for each user. Then that is joined to the images table to get the other details for that image from the images table.
Note I have used LEFT OUTER JOIN. This is to return a row for a user who doesn't have any images uploaded.
Note it is generally a bad idea to use SELECT *, rather than specifying the columns to be returned. I have left this in place here as I do not know the column names of your tables.

Related

Mysql query does not look right

I want to update a field in my table based in another table and I executed this query below but I think it's not right.. it looks like it worked but is it correct? Is there any situation where it might fail?
UPDATE users SET page = (SELECT page_name FROM pages WHERE user_id = id)
My table USERS has a column id and page. My table PAGES has a column page_name and user_id. Is the code above right?
It may fail if pages has more than one page_name per user_id. I find UPDATE a INNER JOIN b ON some_conditions SET a.fieldA = b.fieldB; to be much more readable. It does have the same failure scenario, and can be harder to "fix" for such scenarios; but correlated subqueries (your version) tend to be significantly slower.
Also, style note, UPDATE users AS u SET u.page = (SELECT p.page_name FROM pages AS p WHERE p.user_id = u.id); would've eliminated the need for your last two sentences and (more importantly):
make it so the next developer that has to look at the query does not have to look at the database to find out (or remember) what fields go to what tables.
make it so the query does not break if an id field later gets added to pages.
Instead of subselect In mysql you can use UPDATE JOIN
UPDATE users
INNER JOIN pages on pages.user_id = users.id
SET users.page = pages.page_name
Whenever you have more than one table in a query, you should always use qualified column names -- and preferably aliases. So, your version of the query would be:
UPDATE users u
SET page = (SELECT p.page_name FROM pages p WHERE p.user_id = u.id);
Next, you have to consider whether the subquery might return more than one row. If so, you have to limit it to one row. There are various ways, SELECT MAX(p.page_name), LIMIT 1, and SELECT GROUP_CONCAT(p.page_name) all come to mind.
Next, you are updating all rows in users. If you only want to update matching rows, then you can continue on the subquery path using IN or EXISTS in the WHERE clause. Alternatively, use JOIN:
UPDATE users u JOIN
pages p
ON p.user_id = u.id
SET u.page = p.page_name;
But most importantly, ask the existential question: Why do you need to do this update? You have a link between the two tables. Use the link instead of storing the name:
select u.*, p.page_name
from users u left join
pages p
on p.user_id = u.id;
You can use the below sample SQL and change as per your requirement.The Code above seems correct .Could you please paste the error
UPDATE TableB
SET TableB.value = (
SELECT TableA.value
FROM TableA
WHERE TableA.name = TableB.name
);

JOIN - Select columns/details with different criteria than COUNT

I have a chat system. There are 3 tables:
data_chats - holds the IDs of the chats themselves. This is where you mark a chat as deleted.
data_chat_parties - holds the member or team ID that is included in chat along with chat permissions, who they were invited by, etc
data_chat_messages - holds the actual messages of chats
With my query, I am trying to fetch the info from data_chat_parties related to the party requesting this information (ie currently logged in user), but also get the total number of chat parties in the chat.
SELECT
data_chats.id AS chat,
data_chats_parties.*,
COUNT(data_chats_parties.id) AS total_parties,
data_chats_messages.created AS last_message_created,
data_chats_messages.author AS last_message_author,
data_chats_messages.author_type AS last_message_author_type,
data_chats_messages.message AS last_message
FROM data_chats
LEFT JOIN data_chats_parties ON data_chats_parties.chat=data_chats.id
LEFT JOIN data_chats_messages ON data_chats_messages.chat=data_chats.id AND data_chats_messages.active=1
WHERE
data_chats.active=1 AND
data_chats_parties.member=1 AND
data_chats_parties.status >= 1
GROUP BY data_chats_parties.chat
ORDER BY last_message_created DESC
This all works fine, except that total_chat_parties always returns 1, presumably because it's only matching the record of data_chats_parties.member=1. How would I fetch the party record specific to this user but at the same time, fetch the total number of parties for this chat?
You should use a correlated query :
SELECT data_chats.id AS chat,
(SELECT COUNT(data_chats_parties.id) FROM data_chats_parties
WHERE data_chats_parties.chat = data_chats.id) AS total_parties,
data_chats_messages.created AS last_message_created,
data_chats_messages.author AS last_message_author,
data_chats_messages.author_type AS last_message_author_type,
data_chats_messages.message AS last_message
FROM data_chats
LEFT JOIN data_chats_messages
ON data_chats_messages.chat = data_chats.id
AND data_chats_messages.active = 1
AND data_chats_parties.member = 1
AND data_chats_parties.status >= 1
WHERE data_chats.active = 1
ORDER BY last_message_created DESC
Another thing is the conditions on the WHERE clause, you can filter the RIGHT table of a LEFT JOIN in the WHERE clause, those condition should only be specified in the ON clause.
You also group by a column from the RIGHT table - this is not suggested at all! Either use an inner join, or group by another field.
you may be able to use a subquery in the select statement to give you the desired count.
(select COUNT(data_chats_parties.id) from data_chats_parties where data_chats_parties.chat=data_chats.id) AS total_parties,
Also you can then remove the line
LEFT JOIN data_chats_parties ON data_chats_parties.chat=data_chats.id
Hopefully I've typed that all correctly =)

SUM value from query changes when i add inner join to the query

$sql = mysql_query("SELECT totals.*, sum(totals.payments) as total_payments
FROM totals
INNER JOIN users
GROUP BY totals.idseller;");
When i add the INNER JOIN the sum value is changed. Why?
In my SQL table i have one record in totals width this value: 8943.09 but when i do the some the result is giving me this value: 44715.45
What i am doing wrong?
$sql = mysql_query("SELECT totals.*, sum(totals.payments) as total_payments FROM totals
INNER JOIN users ON totals.idseller = users.idseller
GROUP BY users.UserName;");
Use this Hope this will help you.
When you INNER JOIN to another table, the returned data set is modified to only include rows that exist in both tables. In this case it is likely that there are rows in 'totals' that do not have a matching row in users - either the totals.idseller field might accept null values, or data has become orphaned when matching users have been deleted or edited.
If you want all data in 'totals' regardless of matching user you would user a LEFT JOIN instead in ms-sql, I suspect a similar approach will work in my-sql
You should give an "on" based on the ids. Such as like
inner join users on users.id = totals.idseller
Otherways the sql server will combine all possible rows in the tables, which is most cases not what you wish.
Because when you are adding inner join in your SQL Query, it means you are selecting the data which is common in both the tables.
EX:
SELECT * FROM TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.ID = TABLE_B.ID
If you are joining users table which contains 5 records. By joining table, as there is no any column mapping, this sum-up 5 times and this is reason for showing different values.
Please let me know something wrong in it.
Thanks,
Umehs

how to write mysql query with join and still display partial info if a record from one table is deleted?

i have a join query that displays info from 2 tables, but if a record is deleted from one of the tables, the joined record doesn't show. i still want to display the joined record but with the data from the first table showing and the info with the second table missing.
here is the query i'm working with.
$result = mysql_query("SELECT user_groups.*, pricing_groups.* FROM user_groups inner join pricing_groups on user_groups.pricing_group_id = pricing_groups.id LIMIT 2,18446744073709551615")
i'm limiting it by 2. i still want that in next version.
if record from pricing_groups is deleted that is attached to a record from user_groups, it's ok to show the user group info. in fact, i want it to display the user group info. but if it's the other way around...if a user group is deleted, then it's ok not to show either the user group info or pricing group info.
To include all records from user_groups you would need to use a LEFT JOIN like so:
SELECT ug.*, pg.*
FROM user_groups AS ug
LEFT JOIN pricing_groups AS pg
ON ug.pricing_group_id = pg.id
LIMIT 2,18446744073709551615
The LEFT JOIN returns all rows from the left table (user_groups), with the matching rows from the right table (pricing_groups). The result is NULL in the right side when there is no match.

Mysql relationships and getting linked data

I have been doing this for a while now, via some php, first lets say we have two tables:
Users
user_id name email
Images
image_id user_id url
user_id and user_id from images table would be linked with a relationship.
Now what I would do is select the user by their Id, check if the user is found, if so then make another query to images table, and check for num rows and loop through the return, is there a function that I could use that would allow me to just select the user and all the images that are linked to the user without doing a joint query.
Thank you for any help
When you say "without doing a joint query" I think you mean "without doing two queries."
In fact, what you want is probably a LEFT JOIN. The idea is that you select users from the user table matching some ID, and LEFT JOIN the images table. The left join will give you null values if no images exist for the user. If you use a normal join, the fact that no matching records exist in the images table will result in no rows returned.
Here is an example:
SELECT u.name, u.email, i.url
FROM Users u
LEFT JOIN Images i ON (i.user_id = u.user_id)
WHERE u.id = #SpecificUserID;
Assuming the user id is found and there are some images for that user, you will get a result that looks like this:
name email url
----- ----- -----
John j#a.com abc.jpg
John j#a.com def.jpg
John j#a.com ghi.jpg
Now as you can see, the name and email values keep repeating. You get a unique image url for each row and the matching username and email.
If you only select one user at a time, this is simple to process in a loop. On the first iteration read all three values. On subsequent iterations just read the url, adding it to your list or array.
Here is a useful tutorial on joins: Understanding JOINs in MySQL and Other Relational Databases
This can be done in one query rather than two by using an inner join to get your result set.
$sql = "SELECT u.user_nid, i.url
FROM tbl_user u
INNER JOIN i.user_nid = u.user_nid
WHERE user_nid = ?"
With this query you will receive a list of the users images and if there are no images returned or the user does not exist, than you will have a row return of zero.
You'll have to use a join to retrieve data from multiple tables in a single query.
The foreign key relationships enforce constraints. Ex: You can't insert a record into Table A referring to a key in Table B without the record actually being in Table B.

Categories