SQL Union or other table join - mysql

I'm having trouble wrapping my head around an SQL query, trying to figure out something.
Just quickly, here is my (simple) table:
It's a representation of friends added in a social network site.
I have to use the table format like this, user1 and user2 are both primary keys.
User1 | User2
--------------------------------------
foo#a.com | things#stuff.com
foo#a.com | lala#hi.com
things#stuff.com| lol#k.com
lol#k.com | foo#a.com
foo#a.com | lol#k.com
What I need to do is write a SELECT statement that will return all unique users that are friends with foo#a.com for example.
SELECT User2
FROM members
WHERE User1 = 'things#stuff.com'
Would return lol#k.com and not foo#a.com, even though I need it to return the latter.
SELECT *
FROM members
WHERE User1 = 'foo#a.com'
OR User2 = 'foo#a.com'
Would return an error I think? Something with selecting multiple columns probably.
So I figure a union join or some other join is necessary (union so no duplicates?) but I'm really not sure how to go about doing it.
I'm trying to implement this in PHP, so even if the last query I wrote worked, I'm not sure how to echo User1 or User2 depending on which it returned/which one I needed if that makes sense.

Using a UNION (for performance) :-
SELECT User2
FROM members
WHERE User1 = 'foo#a.com'
UNION
SELECT User1
FROM members
WHERE User2 = 'foo#a.com'

Use an alias name for the column
SELECT User2 AS friend
FROM members
WHERE User1 = 'things#stuff.com'
UNION
SELECT User1 AS friend
FROM members
WHERE User2 = 'things#stuff.com'
Now you can echo friend.
HTH

It all depends on how you define the relationship between the two users.
For example, you have a table entry where User1 = 'foo#a.com' and User2='mumu#f.com'. What does it mean? Does it mean that user foo became friends with mumu? Does it matter the direction of the 'friendship'?
If the answer is no an you simply want to fetch all rows where either user is foo, then your query
SELECT *
FROM members
WHERE User1 = 'foo#a.com'
OR User2 = 'foo#a.com'
is correct.
However, to fetch the data you need to explore both columns for results.

Related

Can I make a statement with two WHEREs and value of second where coming from 3rd table?

I have 3 tables. First one is users, with different users on it. Second one has posts on it, with posters ID from table1 and the contents of the post.
Third table has the friendslist. In the form of:
+------------+----------+
| userID | friendID |
+------------+----------+
| 4 | 3 |
| 3 | 4 |
| 1 | 3 |
| 3 | 1 |
+------------+----------+
Now what I am trying to figure out is how to make it so that when user1 looks at the posts he only sees the posts of only his friends.
I got as far as creating this:
SELECT posts.IDpost, users.name, posts.created, posts.content FROM users JOIN posts ON users.id=posts.poster WHERE posts.poster= ?
I figured I would have to add something like this:
WHERE friendslist.user= user1;
But I can't figure out how to add it, tried google searching for it with no luck, I don't know if I simply can't figure out how to search for it or it is not possible. I probably have to use a JOIN at the end there, but again, I'm not sure how.
So how could I join values from 3 tables, with 2 WHERE commands?
Hope someone understands my question and is able to help.
SELECT posts.IDpost, users.name, posts.created, posts.content
FROM users
JOIN posts
ON users.id=posts.poster
WHERE posts.poster in (
SELECT friendID
FROM friendslist
WHERE userID = user1 -- This would be the ID of the user you're querying for.
)
That's SQL Server syntax, but the idea should be the same.

Filter register from two tables

I am new here and with PHP/MySQL programming. I need do the next consult in my database.
I have two tables:
-The first of relations between users where friends have the value "2"
The columns is like:
id | Username1 | username2 | relation
-The seconds is for messages post by users
The columns is like:
id | username | message
The id is the "primary key" with auto increment property. (in both)
I want to show all message of friends of one user by order of post (Most recently first)
So I need select the max value id where the user1 and user2 is friends, but I need select the max value of "messages table" and check if they are friends in the "relations table"
I know what the next is not the syntax but I need something like this:
SELECT message
FROM message_table
WHERE MAX(id) AND ( FROM relations
WHERE ( ( user1="randomuser"
OR user2="randomuser")
AND relation="2")
)
ORDER BY id DESC
I know what that is wrong... but I need that two filters to show all friend message in posting order.. So how can I show register from one table with a one condition in that table and the second condition in other table.
Edit: Example:
id | Username1 | username2 | relation
1 Randomuser1 Randomuser2 2
2 Randomuser1 Randomuser3 0
3 Randomuser1 Randomuser5 2
id | Username | message
1 Randomuser1 Hi this is a random message?
2 Randomuser2 I don"t like your message
3 Randomuser5 Sure, is a bad message
So I want to show all message of friends of randomuser1 by order of posting
First the user"s friends of Randomuser1 is
Randomuser2 and Randomuser5 (The "2" Mean they are friends)
so i want to show all messages of their friends by order of posting (By id order of second table (message_table))
So i need a output like:
id | Username | message
3 Randomuser5 Sure, is a bad message
2 Randomuser2 I don"t like your message
That is the output what I need when I choose the messages of friends of user Randomuser1

How can I optimize this SQL query with a large IN clause?

I have a fairly complicated operation that I'm trying to perform with just one SQL query but I'm not sure if this would be more or less optimal than breaking it up into n queries. Basically, I have a table called "Users" full of user ids and their associated fb_ids (id is the pk and fb_id can be null).
+-----------------+
| id | .. | fb_id |
|====|====|=======|
| 0 | .. | 12345 |
| 1 | .. | 31415 |
| .. | .. | .. |
+-----------------+
I also have another table called "Friends" that represents a friend relationship between two users. This uses their ids (not their fb_ids) and should be a two-way relationship.
+----------------+
| id | friend_id |
|====|===========|
| 0 | 1 |
| 1 | 0 |
| .. | .. |
+----------------+
// user 0 and user 1 are friends
So here's the problem:
We are given a particular user's id ("my_id") and an array of that user's Facebook friends (an array of fb_ids called fb_array). We want to update the Friends table so that it honors a Facebook friendship as a valid friendship among our users. It's important to note that not all of their Facebook friends will have an account in our database, so those friends should be ignored. This query will be called every time the user logs in so it can update our data if they've added any new friends on Facebook. Here's the query I wrote:
INSERT INTO Friends (id, friend_id)
SELECT "my_id", id FROM Users WHERE id IN
(SELECT id FROM Users WHERE fb_id IN fb_array)
AND id NOT IN
(SELECT friend_id FROM Friends WHERE id = "my_id")
The point of the first IN clause is to get the subset of all Users who are also your Facebook friends, and this is the main part I'm worried about. Because the fb_ids are given as an array, I have to parse all of the ids into one giant string separated by commas which makes up "fb_array." I'm worried about the efficiency of having such a huge string for that IN clause (a user may have hundreds or thousands of friends on Facebook). Can you think of any better way to write a query like this?
It's also worth noting that this query doesn't maintain the dual nature of a friend relationship, but that's not what I'm worried about (extending it for this would be trivial).
If I am not mistaken, your query can be simplified, if you have a UNIQUE constraint on the combination (id, friend_id), to:
INSERT IGNORE INTO Friends
(id, friend_id)
SELECT "my_id", id
FROM Users
WHERE fb_id IN fb_array ;
You should have index on User (fb_id, id) and test for efficiency. if the number of the itmes in the array is too big (more than a few thousands), you may have to split the array and run the query more than once. Profile with your data and settings.
Depends on if if the following columns are nullable (value can be NULL):
USERS.id
FRIENDS.friend_id
Nullable:
SELECT DISTINCT
"my_id", u.id
FROM Users u
WHERE u.fb_id IN fb_array
AND u.id NOT IN (SELECT f.friend_id
FROM FRIENDS f
WHERE f.id = "my_id")
Not Nullable:
SELECT "my_id", u.id
FROM Users u
LEFT JOIN FRIENDS f ON f.friend_id = u.id
AND f.id = "my_id"
WHERE u.fb_id IN fb_array
AND f.fried_id IS NULL
For more info:
http://explainextended.com/2010/05/27/left-join-is-null-vs-not-in-vs-not-exists-nullable-columns/
http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/
Speaking to the number of values in your array
The tests run in the two articles mentioned above contain 1 million rows, with 10,000 distinct values.

In MySQL, How do you dynamically SELECT the values of one table, as columns in another?

I'm no MySQL guru, but I get around the basic stuff pretty well. Thanks for your feedback.
I have two tables user and favorite. Each user can have multiple unique favorites.
table user u
[ user_id + name ]
100 | Sally
table favorite fav
[ fav_id + user_id + fav_key + fav_val ]
1 | 100 | icecream | mint
2 | 100 | candybar | snickers
3 | 100 | color | red
I want to create a SELECT statement that will turn the user's favorites into columns using the fav_key value as the column header. *The problem is I will never know what the fav_val value will be as these are user entered, so the column names have to be generated dynamically.
SELECT ...
[ user_id + name + fav_icecream + fav_candybar + fav_color ]
100 | Sally | mint | snickers | red
With some distant thought of performance in mind -- one of the issues is that I don't want to run two SELECT statements to get the user data and the user favorites (plus I like the idea of having the columns dynamically named in this way).
UPDATE
So this is called, pivoting, excellent.
What if I don't ever know what the values are? I need to dynamically generate the columns from a query on favorites.
Like this:
Select fav.user_id, u.name
MAX(case WHEN fav.fav_key = 'icecream' then fav.fav_val end) as 'fav_icecream',
MAX(case WHEN fav.fav_key = 'candybar' then fav.fav_val end) as 'fav_candybar',
MAX(case WHEN fav.fav_key = 'color' then fav.fav_val end) as 'fav_color'
From favorite fav
inner join users u on fav.user_id = u.user_id
group by fav.user_id
Working Example: DEMO
Note that: the demo is for MS SQL Server but it is working the same way for mysql, I just tried to give you a live demo.
Essentially you want PIVOT functionality. Unfortunately, that's not natively supported by MySQL (unlike Oracle or SQL Server). There are many questions on StackOverflow showing how to work around that lacking functionality of MySQL:
https://stackoverflow.com/search?q=mysql+pivot+sql
Some examples:
pivot in mysql queries
MySQL Query and Pivot Tables

complex sql query issue

I have a little SQL but I can't find the way to get back text just numbers. - revised!
SELECT if( `linktype` = "group",
(SELECT contactgroups.grname
FROM contactgroups, groupmembers
WHERE contactgroups.id = groupmembers.id ???
AND contactgroups.id = groupmembers.link_id),
(SELECT contactmain.contact_sur
FROM contactmain, groupmembers
WHERE contactmain.id = groupmembers.id ???
AND contactmain.id = groupmembers.link_id) ) AS adat
FROM groupmembers;
As now I have improved a bit gives back some info but ??? (thanks to minitech) indicate my problem. I can't see how could I fix... Any advice welcomed! Thansk
Contactmain (id, contact_sur, email2)
data:
1 | Peter | email#email.com
2 | Andrew| email2#email.com
Contactgroups (id, grname)
data:
1 | All
2 | Trustee
3 | Comitee
Groupmembers (id, group_id, linktype, link_id)
data:
1 | 1 | contact | 1
2 | 1 | contact | 2
3 | 2 | contact | 1
4 | 3 | group | 2
And I would like to list out who is in the 'Comitee' the result should be Andrew and Trustee if I am right:)
It does look a bit redundant on the join since you are implying both the ID and Link_ID columns are the same value. Since BOTH select values are derived from a qualification to the group members table, I have restructured the query to use THAT as the primary table and do a LEFT JOIN to each of the other tables, anticipating from your query that the link should be found from ONE or the OTHER tables. So, with each respective LEFT JOIN, you will go through the GroupMembers table only ONCE. Now, your IF(). Since the group members is the basis, and we have BOTH tables available and linked, we just grab the column from one table vs the other respectively. I've included the "linktype" too just for reference purposes. By using the STRAIGHT_JOIN will help the engine from trying to change the interpretation of how to join the tables.
SELECT STRAIGHT_JOIN
gm.linktype,
if( gm.linktype = "group", cg.grname, cm.contact_sur ) ADat
from
groupmembers gm
left join contactgroups cg
ON gm.link_id = cg.id
left join contactmain cm
ON gm.link_id = cm.id
If contactgroups.id must equal groupmembers.id but must also equal 2, that's redundant and also probably where your problem is. It works fine as you've written it: http://ideone.com/7EGLZ so without knowing what it's actually supposed to do I can't help more.
EDIT: I'm unfamiliar with the comma-separated FROM, but it gives the same result since you don't select anything from the other table so it doesn't really matter.