MySQL: Join two tables - sql

I'm having problems with design correct SQL query. I'm tired like a hell today, been working over 12 horus (deadline soon) and cannot find issue...
Tables:
buddies | userid | buddyid
users | id | username
Now, what I'd like to do:
Query table buddies for all user friends (when ID = userid OR ID = buddyid). Having no problems with that.
Problem comes when I try to join users table to get username, username is NULL, can't find out why.
Would you like to help me?
Here's working query (but returning empty username)
SELECT username
FROM (
`users_buddies`
)
LEFT JOIN `users` ON ( 'users.id' = 'buddyid'
OR 'users.id' = 'userid' )
WHERE `userid` =1
OR `buddyid` =1
Thanks in advance for any help. I'm more than sure it's tiny bug (caused by me) but really cannot find it. Spent over one hour on this, then decided to ask.
Regards,
Tom

think it's the quotes, try this:
SELECT username
FROM users_buddies ub
LEFT JOIN users u
ON u.id In (ub.userId, ub.buddyid)
Secondly, your Where condition doesn't make sense. If you only want one name to come up then you can restrict it to userid = 1 or buddyId = 1.. (that's the same user, whether he's a user in user_buddies, or a buddy in user_buddies)
If what you want is to find all the buddies of user with userid = 1 , then try this:
SELECT b.username
FROM users_buddies ub
LEFT JOIN users b
ON b.id = ub.buddyid
Where ub.userid = 1
or even better,
Select u.username User, b.username Buddy
From users_buddies ub
LEFT JOIN users u
ON u.id = ub.userid
LEFT JOIN users b
ON u.id = ub.buddyid
Where ub.userid = 1

Tiny bug is that you are using incorrect quotes in ON condition
change
'users.id' = 'buddyid' OR 'users.id' = 'userid'
to
`users`.`id` = `buddyid` OR `users`.`id` = `userid`

Why do you use ` character for object names?
SELECT username
FROM users_buddies
LEFT JOIN users ON ( users.id = 'buddyid'
OR users.id = 'userid' )
WHERE userid =1
OR buddyid =1

Now everyone has spotted it, oughtn't it be this?
SELECT username
FROM users_buddies LEFT JOIN
users ON (users.id = buddyid OR users.id = userid)
WHERE userid = 1 OR
buddyid = 1;
What is the idea of all these ` marks, anyway?

As other people have observed, you're using the wrong quotes.
The ' character is for quoting strings, not objects. So, you're doing a pair of string comparisons. The string 'user.id' will never equal the string 'buddyid', and ditto for your other comparison, so the query returns nothing.
For quoting objects (tables, columns, etc) you must use the backquote character `.
You are only required to quotes objects if the object is named the same as a reserved word or has non-standard characters (spaces and such) in it. So, in this query, none of the quote characters are actually required.
IMO, if you must quote an object, that's a good sign that you shouldn't use that particular name.

Related

trouble with inner joining 2 tables

I have a database with 2 tables in it one is 'enlistments' and the other one is 'users'. In the enlistments table I have a user_id and in the users table I have a name. I want to get the name of the user which belongs to the id.
I know I need to do this with an inner join like this:
SELECT enlistments.round_id, users.name
FROM enlistments
INNER JOIN users
ON enlistments.user_id=users.name
WHERE enlistments.activity_id = 1;
However I get this error: Warning: #1292 Truncated incorrect DOUBLE value
I did some research and found out it has to do with comparing an int with a string but I don't know how to solve the problem.
This is how my database looks like
join on is the condition you use to join the tables. Here it's enlistments.user_id=users.id.
select e.round_id
,u.name
from enlistments e join users u on u.id = e.user_id
where activity_id = 1
round_id
name
1
test2
Fiddle
To validate and be sure you are pulling back the exact data desired, I usually provide aliases for each column brought back and make sure to bring back the join columns also. It's good practice to label where the columns returned originated.
SELECT
Enlistments.UserID as Enlistments_UserID,
Users.ID as Users_ID,
enlistments.round_id as Enlistments_RoundID,
users.name as Users_Name
FROM enlistments
INNER JOIN users
ON enlistments.user_id=users.id
WHERE enlistments.activity_id = 1;
SELECT EN.round_id, US.name
FROM enlistments EN
INNER JOIN users US
ON US.name= CAST(EN.user_id AS VARCHAR)
WHERE EN.activity_id = 1
What you are needing is the function cast that can convert any kind of data into another, so you'll pass your integer value as the first argument followed by "AS '%DATATYPE'" where %DATATYPE is the kind of data you want to achieve.
In your case:
SELECT CAST(123456 AS VARCHAR)
-- RETURNS : '123456'
Anyway, I’m not sure that you can be able to join these two tables with the join you are using.
For more help please share some data.

SQL SELECT * FROM 2 tables

I am building a small database app for friends where table 1 is contacts and table 2 is users. I can find email on both (One as the loggued in user and the other as the owner of the contact)
SELECT *
FROM contacts
WHERE contacts.username = users.email
I try to show all contacts fields where username is equal to already loggued in users (email)
Thanks you very much!
It sounds like you're trying to JOIN two tables together. Ideally, you don't want to use the email as the primary key on a table (the smaller the data, the faster your JOIN will be); a better option would be to add an auto-incrementing Id (integer) to both the Contacts and Users tables, set as the primary key (unique identifier). Joining on integers is much faster, as integers are 4 bytes per row, vs string which (in MySQL) is 1 per character length (latin1 encoding) + 1 byte.
Anyway, back to the original question. I believe the query you're looking for (MySQL syntax) is:
SELECT c.Id, c.Col1, u.Col2, ...
FROM contacts AS c
INNER JOIN users AS u ON u.email = c.username
Additionally, I would avoid the use of *, as it slows down the query a bit. Instead, try to specify the exact columns you need.
Try the following. Also, I would suggest you learn about joins in SQL.
SELECT *
FROM contacts
INNER JOIN
users on contacts.username = users.email
Use Inner Join:
SELECT *
FROM contacts as c
INNER JOIN
users as u on u.email = c.username

Problems with joining two tables in SQL

I have two tables. user(user_id, username, password, age) and comment(comment_id, comment, user_id(foreign key)).
I'm trying to get username from user, using the user_id provided in comment.
My query looks like this:
$sql = "SELECT username FROM user WHERE user_id = (SELECT user_id FROM comments)";
I'm getting null. Is my brain working poorly or is it something else I messed up?
I just want to display all comments after each other, with the username before it.
Use IN instead of "=" .
SELECT username FROM user WHERE user_id IN (SELECT user_id FROM comments);
OR you can use a proper join, something like:
SELECT username FROM user,comments WHERE user.user_id = comments.user_id
That's not a join - a join would be:
$sql = "SELECT username FROM user u JOIN comments c ON u.user_id = c.user_id";
When you use a subquery with =, the subquery must return one value. To show all related records in a related table, use JOIN instead.

Oracle SELECT WHERE value exists or doesn't exist

I have 3 tables; CASES, USERS and USER_META. For this issue you need to know that the USER_META table has 3 columns; user_id, meta_key and meta_value
Each user is associated with many CASES and each USER is associated with many USER_META
My current query is like this
SELECT CASES.*, USERS.*, USER_META.*
FROM CASES
JOIN USERS ON USERS."user_id" = CASES."user_id"
JOIN USER_META ON USER_META_"user_id" = USERS."user_id"
The problem with this approach is that each USER has A LOT of USER_META so my result set has too many rows. How can I rewrite this query so that I can select only the USER_META where the USER_META."meta_key" is equal to a certain value yet still get the result if they do not have this USER_META."meta_key" set yet
For example:
SELECT CASES.*, USERS.*, USER_META.*
FROM CASES
JOIN USERS ON USERS."user_id" = CASES."user_id"
JOIN USER_META ON USER_META_"user_id" = USERS."user_id"
WHERE USER_META."meta_key" = 'my_key'
This would work great but not all users have a value of "my_key" in the "meta_key" column and we still need to view their CASE. For users that do not have the "meta_key" the result should just return the CASE and USER columns.
How can I rewrite this query so it gets the result for both users with this meta_key and without?
Thanks, I hope this makes sense.
I would use a LEFT JOIN
SELECT CASES.*, USERS.*, USER_META.*
FROM CASES
JOIN USERS ON USERS."user_id" = CASES."user_id"
LEFT JOIN USER_META ON USER_META."user_id" = USERS."user_id" AND USER_META."meta_key" = ?
you need to use OUTER JOIN with the table that may have no results. In Oracle, use (+) near to the field name of the join sentence with this table. This link may help you: http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/queries006.htm

SQL - re-arrange a table via query

I have a poorly designed table that I inherited.
It looks like:
User Field Value
-------------------
1 name Aaron
1 email aaron#company.com
1 phone 800-555-4545
2 name Mike
2 email mike#group.org
2 phone 777-123-4567
(etc, etc)
I would love to extract this data via a query in the more sensible format:
User Name Email Phone
-------------------------------------------
1 Aaron aaron#company.com 800-555-4545
2 Mike mike#group.org 777-123-4567
I'm a SQL novice, but have tried several queries with variations of Group By,
all without anything even close to success.
Is there a SQL technique to make this easy?
this not a 'badly designed table'; but in fact an Entity Attribute Value (EAV) table. unfortunately, relational databases are poor platforms to implement such tables, and negate most of the nice things of RDBMS. A common case of using the wrong shovel to nail in a screw.
but i think this would work (based on Marcus Adams' answer, which i don't think would work (edit: now it does))
SELECT User1.Value AS name, User2.Value AS email, User3.Value AS phone
FROM Users User1
LEFT JOIN Users User2
ON User2.User = User1.User AND User2.Field='email'
LEFT JOIN Users User3
ON User3.User = User1.User AND User3.Field='phone'
WHERE User1.Field = 'name'
ORDER BY User1.User
Edit: got some niceties from other answers (LEFT Joins, and the field names on the ON clauses), now does anybody know how to put the remaining WHERE a little higher? (but not on the first JOIN's ON, that's too ugly), of course it doesn't matter since the query optimizer uglyfies it back anyway.
At my work we are unfortunate to have a database design like this. But this kind of design works better for us then a traditional database design because of the different records we have to store and gives us the flexibility that we need. The database that we are using stores millions of records.
This would be the fastest way to run the query on a large database using MSSQL. It saves from having to do as many joins which could be very costly.
DECLARE #Results TABLE
(
UserID INT
, Name VARCHAR(50)
, Email VARCHAR(50)
, Phone VARCHAR(50)
)
INSERT INTO #Results
SELECT DISTINCT User FROM UserValues
UPDATE
R
SET
R.Name = UV.Value
FROM
#Results R
INNER JOIN
UserValues UV
ON UV.User = R.UserID
WHERE
UV.Field = 'name'
UPDATE
R
SET
R.Email = UV.Value
FROM
#Results R
INNER JOIN
UserValues UV
ON UV.User = R.UserID
WHERE
UV.Field = 'Email'
UPDATE
R
SET
R.Phone = UV.Value
FROM
#Results R
INNER JOIN
UserValues UV
ON UV.User = R.UserID
WHERE
UV.Field = 'Phone'
SELECT * FROM #Results
You can use a self join:
SELECT User1.User, User1.Value as Name, User2.Value as Email,
User3.Value as Phone
FROM Users User1
JOIN Users User2
ON User2.User = User1.User
JOIN Users User3
ON User3.User = User1.User
WHERE User1.Field = 'name' AND User2.Field = 'email' AND User3.Field = 'phone'
ORDER BY User1.User
I tested this query, and it works.
I believe this will build the result set you're looking for. From there, you can create a view or use the data to populate a new table.
select user, name, email, phone from
(select user, value as name from table where field='name')
natural join
(select user, value as email from table where field='email')
natural join
(select user, value as phone from table where field='phone')
In MySQL you can do something like this:
SELECT
id,
group_concat(CASE WHEN field='name' THEN value ELSE NULL END) AS name,
group_concat(CASE WHEN field='phone' THEN value ELSE NULL END) AS phone,
...
FROM test
GROUP BY id
The aggregate function actually doesn't matter, as long as you have only one field of each type. You could also use min() or max() instead with the same effect.
A variant of Javier's answer, which has my vote.
SELECT
UserName.name, UserEmail.email, UserPhone.phone
FROM
Users AS UserName
INNER JOIN Users AS UserEmail ON UserName.User = UserEmail.User
AND UserName.field = 'name' AND UserEmail.field = 'email'
INNER JOIN Users AS UserPhone ON UserName.User = UserPhone.User
AND UserPhone.field = 'phone'
Use LEFT JOINs if not all attributes are guaranteed to exist. A composite index over (User,Field) would probably be beneficial for this.