PHP/MySQL Many to Many. How? - mysql

I recently thought that storing an array in MySQL database would solve my problems... then I went and banged my head on a wall until I realised that there was an easier solution to my problem. Unfortunately I can't seem to figure out what the hell I need to do.
I have two tables. User and Entry. Each user can be the Author of any number of entries. One to many right? Well the problem comes from the fact that each entry can have many users. -_-;
As you can imagine, doing this using arrays would be...not only cumbersome but also stupid, especially if someone managed to accumulate 2000 entries. I had anticipated that I might need to create an extra table and some resources do seem to suggest it but the resources aren't as clear as I would have hoped. I will need to get all the users for a post and somewhere else, I'll need to get all the posts for a user.
Can someone explain the best way of doing this? (Without using arrays of course =p)

you need to make 3 tables:
user (primary key = id)
id
name
...
entry (primary key = id)
id
text
...
userentry (primary key = complex key user,entry)
user
entry
if you want to get an entrys users, just do
SELECT
user.*
FROM
userentry
INNER JOIN
user
ON
userentry.user = user.id
WHERE
userentry.entry = [your entry-id]
and to get all entrys of a user, just do it the other way around:
SELECT
entry.*
FROM
userentry
INNER JOIN
entry
ON
userentry.entry = entry.id
WHERE
userentry.user = [your user-id]

The best way to do this is to have three tables:
Users:
Id,
Name,
...
Posts:
Id,
PostName,
...
UserPosts:
Id surrogate key,
UserId,
PostId.
Then to get all the users for a post
SELECT u.*
FROM users u
INNER JOIN posts p ON u.id = p.userid
WHERE p.id = apostid
To get all the posts for a user:
SELECT p.*
FROM posts p
INNER JOIN users u ON u.id = p.userid
WHERE u.id = auserid

Related

Joining on the same table more than once

I'm probably being a bit dumb, hopefully someone can help.
I have a simple 2 column user table (ID, USERNAME).
I have a comments table for images (COMMENT, COMMENTFROM, COMMENTTO)
COMMENTFROM is the ID of the user who made the comment. COMMENTTO is the ID of the owner of the image that the comment was added to. Both users are held within the USERS table.
I want to pull out and display rows like this
"really nice photo" - to USERXYZ - from USER123**
This has puzzled me, because if I join the USERS table to the comments table on:
WHERE comments.userfrom = users.id
That only gets me one (or the other) of the 2 usernames I need per row. Is there a way I can get both?
I'm not even sure how I would search for this answer on SOF, apologies if it has been answered before. If anyone can point me in the right direction it would be appreciated :)
You need to JOIN to the users table twice, and give them different identifiers (aka aliases) on each JOIN within your SQL.
SELECT
comment,
userFrom.username AS commentFrom,
userTo.username AS commentTo
FROM comments
JOIN users AS userFrom ON userFrom.ID = comment.commentFrom
JOIN users AS userTo ON userTo.ID = comment.commentTo
Please try following one
SELECT CONCAT(c.comment," - to ",
(SELECT USERNAME FROM user WHERE user.ID = c.COMMENTTO LIMIT 1),
" - from ",
(SELECT USERNAME FROM user WHERE user.ID = c.COMMENTFROM LIMIT 1)) FROM comments c

Join on a cyclic relation (beginner)

I'm a beginner in queries and I'm struggling with one of them. Here are the two tables involved :
The askstobefriends table permit a user to add a friend in the application I m developping. The relational form of it is :
AskToBeFriends(ID (long), #UserAsker (long), #UserAsked (long), Accept (tinyInt))
So with this table we can see who asked to be friend and if it was accepted ...
The query I m trying to realize would permit to list all the user's friends from his ID and also return the friendship statut (accept field ==> waiting for an answer, accepted or refused).
Speretaly, it would be something like that :
SELECT Accept, UserAsker, UserAsked
FROM askstobefriends
WHERE UserAsker = '".$userID."' OR UserAsked = '".$userID."' ";
==> first issue : it can either be the user who asked to be friend with someone or the opposit, that why i've put and OR. After that, I d like that for everyfriend founded there's these informations :
SELECT colUserID, colUserLogin, colUserName, colUserFirstname
FROM userTable
WHERE colUserID == FRIEND
So I guess I need to do a join query, and in my join I have to be sure that I'm using the right foreign key from the asktobefriends tablefor each cases !! (once the key could be UserAsked and another time UserAsker depending on who asked to be friends :S )
Does anyone have a clue please :S ?? Thanks ;-) !!
Your design is wrong. A User asks to be friend of another User, so "Ask_to_be_friend" is the relation, and the cardinality is many to many, so the design will looks like this:
User_User_ID is UserAsker.
User_USer_ID1 is UserAskedtobefriend
and the query could be like (you'll get all the users that user_user_ID Asks to be friend of):
Select U.* from User as U
Join Ask_to_be_friend as A on
U.user_ID = A.User_user_ID
--where a.accept=1 if you add this, this will give
--you all the friends ID of the user_ID table User
If you want to get the names or extra info of the askedtobefriend you'll need a extra Join
Select U.* from User as U
Join Ask_to_be_friend as A on
U.user_ID = A.User_user_ID
Join User as U2 on
A.User_User_ID1=u2.User_ID
--where a.accept=1 ,with this you'll with get only the friends
You could join the tables using criteria that ensure only friends of :userID are returned. For example:
SELECT u.*, a.Accept
FROM askstobefriends a JOIN userTable u ON (:userID, u.colUserID) IN (
(a.UserAsker, a.UserAsked),
(a.UserAsked, a.UserAsker)
)

Mysql fetching data from multiple tables

I am having three tables
user(id,fname,lname)
picture(picid,pic_path,userid)
friends(friend_of,friends_id,status)
I want to fetch id,fname,lname,pic_path for all friends_id of friends_of=1
picture path may be null
Thankx in advance.
What you're looking for is called a join. In particular, you're looking for a JOIN and a LEFT JOIN.
SELECT
user.id, user.fname, user.lname, picture.pic_path
FROM
friends
JOIN user
ON user.id = friends_id
LEFT JOIN picture
ON userid = user.id
WHERE
friends.friend_of = 1
This will only work though if there's a maximum of 1 entry in picture.
Though this answers your immediate question, I suggest you become very familiar with joins. They're one of the fundamental operations of a relational database. (Note that a join essentially is a mathematical relation)
Try this
SELECT u.*,p.*,f.*
FROM USER u
LEFT JOIN picture p ON p.user_id = id
INNER JOIN friends f ON f.friends_of = u.id
WHERE f.friends_id = 1
For querys like that you need to understand and employ the relations between your entities. Then you work in two steps: selection and projection and contrary to what SQL-syntax may imply the part before the FROM keyword is the projection.
First we compile data. Clearly we need the data from all three tables and we need it once. So at first we construct all possible combinations, by joining all three tables. In SQL this is done in the FROM part, i.e.
FROM friends f, picture p, user u
f, p and u are aliases which serve the purpose of saving us the efford of typing the full table names in the following.
Now we have all possible combinations. Let's select the ones we want:
I suppose every picture in your DB belongs to a user which is stored in your DB as well. So my assumption is that you only want pictures associated to a user. Hence we have a first restrictions on all the combinations we made before! The restriction derived from the (probable) meaning of the data stored in you database and stated as:
u.id = p.userid
(Notice: by applying this restriction to combination above we "select" only certain combinations.)
Then you already stated another restrictions as a request "friends_of=1" to associate this constraint on the combination we write:
f.friends_of=1
Then we combine your request "friends_of=1" with the other data by:
f.friend_of = u.id
This constraint selects only those users who are a friend of somebody. Now we can combine these constraints. As we want all constraints to be satisfied we AND them in a WHERE statement:
WHERE u.id = p.userid AND f.friend_of = u.id AND f.friends_of=1
The ordering does not affect meaning (in this case. But let's rethink those constraints:
u.id = p.userid : we want information about the user and the pictures associated with that user
f.friend_of = u.id : we are looking for a users who are friends of somebody
f.friends_of=1 : we are looking for friends of a particular somebody
Now we project the data stored in our DB to what we want. We want all the user data and picture paths. In SQL:
SELECT u.*,p.pic_path
Now we put everything together.
SELECT u.*,p.pic_path FROM friends f, picture p, user u WHERE u.id = p.userid AND f.friend_of = u.id AND f.friends_of=1
To allow for friends that don't have a picture associated with them (note: that's very different to pic_path being NULL) you need an outer join, which means you also want combinations with empty sets. That's where my MySQL is not so good but I'd guess you'd generate all combinations you want (and many more) with:
FROM friends f JOIN user u LEFT JOIN picture p ON u.id = p.userid
and
SELECT u.*,p.pic_path FROM friends f JOIN user u LEFT JOIN picture p WHERE f.friend_of = u.id AND f.friends_of=1
Notice, that the constraint that may be violated has been made explicit by moving it from the general selection to the generation of the data combinations as a rule on how to create combinations. And yes, it's a shortcut instead of following through the idea of selection and projection.

Best way to join multiple foreign keys to same table

I have a table "user" with a few rows that are both foreign keys for the same table "content" kinda like this:
user.id
user.bio
user.signature
content.id
content.text
I know this is not the right way to do this from a normalization standpoint, but the "content" table is from a separate DB that I cant modify. And I dont want to duplicate the data.
Im having a problem finding a good way to join them. All I have been able to do is this, but this seems wasteful.
SELECT bio.bio, text.text
FROM(
SELECT content.text as bio
FROM content, user
WHERE user.bio = content.id
AND user.id = 4) AS bio,
SELECT content.text as content
FROM content, user
WHERE user.signature = content.id
AND user.id = 4) AS content
You can join one table multiple times if you give each instance a different alias:
SELECT bio.text, sig.text
FROM user u
JOIN content bio ON u.bio = bio.id
JOIN content sig ON u.signature = sig.id
WHERE u.id = 4

Convert many to many to one to one (mysql)

We changed database schema and moved a relationship between users/accounts from a 1-1 to a many to many using a join table accounts_users.
So we have
accounts,
users,
accounts_users (user_id and account_id)
Our data is still 1-1, and we have decided to move back. So I need sql to move back:
To Migrate I used:
INSERT INTO accounts_users (account_id,user_id) SELECT id AS account_id, user_id AS user_id FROM accounts
To move back I have tried:
UPDATE
accounts
SET
user_id = ru.user_id
FROM
accounts r, accounts_users ru
ON
r.id = ru.account_id
Update accounts
Set r.user_id = ru.user_id
FROM accounts r, accounts_users ru
WHERE r.id = ru.account_id
SELECT accounts_users.user_id
INTO accounts
FROM accounts_users
INNER JOIN accounts
ON accounts.id = accounts_users.account_id
All of these give a sql error of some sort. Im guessing its because my sql is ambiguous, and I need some sort of select first or min or something like that.
** To be clear, Im sure still have the 1-1 relationship in the data, but I cant figure out the sql to bring the data from the existing tables back into the original tables. What im looking for is some working sql that will take the data from accounts_users and put the user_id into the account table. Thanks, Joel
You could try...
UPDATE accounts
SET user_id = (SELECT user_id
FROM accounts_users
WHERE accounts_users.accounts_id = accounts.accounts_id);
That'll get pretty tedious if you have a lot of columns in accounts_users that have to go back in accounts, though, and won't work if there is any problems with the ids (hence my previous answer). How many columns are there?
If your mapping is 1-1 then just select the first result (you know there is only one)