LEFT JOIN and WHERE with NULL values in the result - mysql

A simple query gives me user1's relationship with other users:
select u.id, u.name, ur.*
from users u
left join user_relationships ur
on ((ur.source_user_id = 1 and target_user_id = u.id) OR
(ur.source_user_id = u.id and target_user_id = 1))
where u.id != 1
ORDER BY u.id;
+----+-------+----------+--------+------+
| id | name | rel_from | rel_to | type |
+----+-------+----------+--------+------+
| 2 | beta | 2 | 1 | 2 |
| 3 | gamma | 1 | 3 | 2 |
| 4 | delta | 4 | 1 | 1 |
| 5 | five | NULL | NULL | NULL |
+----+-------+----------+--------+------+
But I only want a list of users with whom the relationship type is not 2 ('delta' and 'five').
I tried some approaches.
-- Approach 1
-- -----------------------------
where
(u.id != 1) AND
(ur.type != 2)
-- gives 'delta', not 'five'
-- Approach 2
-- -----------------------------
left join user_relationships ur
on ((ur.source_user_id = 1 and target_user_id = u.id) OR
(ur.source_user_id = u.id and target_user_id = 1)) AND
(ur.type != 2)
where
(u.id != 1)
ORDER BY u.id;
-- ur.* fields are NULL
-- (all rows, except for 'delta')
-- Approach 3
-- -----------------------------
where
(u.id != 1) AND
((ur.type != 2) OR
(ur.type IS NULL))
-- this works, but why ?
(A) Why Approaches 1,2 don't work, but 3 does ?
(B) Is there another (perhaps more elegant) way to achieve the same result ?

Kaya,
When you work with possible null values should use the IS NULL comparative.
So your where could be:
where
(u.id != 1) AND
(ur.type != 2 OR ur.type IS NULL)

Related

MySQL JOIN using more than one column and table

I have 3 tables that I need to JOIN, but I'm having difficulty getting the right result because I need information from more than 1 column. I wish to get all users that are available and has the status = 1. In addition, get both types 1 and 3. With users listed as type 3, I need to join with the other table (by user id) to get only users with a specific specialty listed on the other table.
My tables are similar to these:
users
id | status | availability | type
1 | 1 | 1 | 1
2 | 1 | 0 | 1
3 | 0 | 0 | 2
4 | 1 | 1 | 3
5 | 1 | 1 | 3
specialties
id | type
1 | 1
2 | 2
3 | 3
4 | 43
My relation table would look like this:
rel_users_specialties
id | id_user | id_specialty
1 | 1 | 29
2 | 2 | 3
3 | 4 | 3
4 | 5 | 3
My query would be:
SELECT *
FROM users u
JOIN rel_users_specialties r ON r.id_user = u.id
WHERE u.status = 1
AND u.disponibilidade = 1
AND r.id_specialty = 3
AND (u.type = 3 OR u.type = 1)
My expected result would be the following users (by id)
1, 4 and 5 (exclude user number 2 because of the availability = 0) and Although the user 1 has a specialty different than 3 it also has a type 1 (type one here would be a constant, regardless of the specialty selected).
Appreciate the help!
Set the conditions like this:
SELECT u.*
FROM users u INNER JOIN rel_users_specialties r
ON r.id_user = u.id
WHERE u.status = 1 AND u.availability = 1
AND ((u.type = 1) OR (u.type = 3 AND r.id_specialty = 3))
Or without a join:
SELECT u.*
FROM users u
WHERE u.status = 1 AND u.availability = 1
AND (
(u.type = 1)
OR
(u.type = 3 AND EXISTS(SELECT 1 FROM rel_users_specialties r WHERE r.id_user = u.id AND r.id_specialty = 3))
)
See the demo.

MySQL count based on condition from a different table

I have the following tables.
Table : types
--------------------
id | type
--------------------
1 | AA
--------------------
2 | BB
--------------------
3 | AA
--------------------
4 | BB
--------------------
Table : users
--------------------
id | username
--------------------
1 | abc
--------------------
2 | bcd
--------------------
3 | cde
--------------------
4 | def
--------------------
Table : methods
---------------------------------
id | user_id | details | type_id
---------------------------------
1 | 1 | detail_1 | 1
---------------------------------
2 | 1 | detail_2 | 3
---------------------------------
3 | 1 | detail_3 | 1
---------------------------------
4 | 1 | detail_4 | 3
---------------------------------
5 | 2 | detail_3 | 1
---------------------------------
6 | 2 | detail_5 | 2
---------------------------------
7 | 2 | detail_6 | 4
---------------------------------
8 | 2 | detail_2 | 3
---------------------------------
9 | 1 | detail_2 | 3
---------------------------------
10 | 1 | detail_2 | 3
---------------------------------
Desired Result :
---------------------------------------------------
UserName | No_of_AA_details | No_of_BB_details |
---------------------------------------------------
abc | 4 | 0 |
---------------------------------------------------
bcd | 2 | 2 |
---------------------------------------------------
I need to get the count of distinct details based on the type from types table.
I have tried this queries but max I am getting is all the counts and not the distinct values.
SELECT u.username,
CASE WHEN t.type = 'AA' THEN count(distinct m.details) END AS No_of_AA_details,
CASE WHEN t.type = 'BB' THEN count(distinct m.details) END AS No_of_BB_details
FROM users as u inner join methods as m on u.id = m.user_id inner join types as t on t.id = m.type_id
GROUP BY m.user_id
SELECT u.username,
SUM(t.type = 'AA') AS No_of_AA_details,
SUM(t.type = 'AA') AS No_of_BB_details
FROM users as u inner join methods as m on u.id = m.user_id inner join types as t on t.id = m.type_id
GROUP BY m.user_id
Any suggestions are welcome.
I can't test it right know but i think you had a good idea, can you try :
SELECT u.username,
m.user_id,
CASE
WHEN t.type = 'AA' THEN 1
ELSE 0
END AS No_of_AA_details,
CASE
WHEN t.type = 'BB' THEN 1
ELSE 0
END AS No_of_BB_details
FROM users as u
INNER JOIN methods as m on u.id = m.user_id
INNER JOIN types as t on t.id = m.type_id
and now you just need to do the sum :
SELECT u.username,
m.user_id,
SUM (CASE
WHEN t.type = 'AA' THEN 1
ELSE 0
END ) AS No_of_AA_details,
SUM (CASE
WHEN t.type = 'BB' THEN 1
ELSE 0
END ) AS No_of_BB_details
FROM users as u
INNER JOIN methods as m on u.id = m.user_id
INNER JOIN types as t on t.id = m.type_id
GROUP BY u.username, m.user_id

Select certain value only

I have 2 table, but I wanted to query the 'rejected' status only,
means I need query the result that the user has only rejected status, instead of having approve & reject, or approve in submissions table
Users Table
-----------
id | name
-----------
1 | John
2 | Doe
3 | Testing
4 | Sample
Submission Table
-------------------------------
id | user_id | title | status
-------------------------------
1 | 1 | title1 | approved
2 | 1 | title2 | rejected
3 | 2 | title3 | approved
4 | 2 | title4 | approved
5 | 3 | title5 | rejected
6 | 3 | title6 | rejected
7 | 3 | title7 | rejected
8 | 4 | title8 | approved
9 | 4 | title9 | approved
10| 4 | title10| rejected
11| 4 | title11| rejected
Below is the result I wanted to achieve :
But I outer join the result query by 'rejected' only but still have some 'approved' result by the users.
but with above query, I'd this result.
What I wanted to query is , query the submissions just have status 'rejected' only, fully ignore the 'approved' , 'approve or reject' result.
I found a solution already which is using WHERE NOT EXISTS to filter the approved result in submission
SELECT u.id AS user_id,s.*, u.name
FROM submissions s
LEFT OUTER JOIN users u
ON s.user_id = u.id
WHERE NOT EXISTS (
SELECT USER_ID
FROM submissions tmp
WHERE tmp.User_ID = s.User_ID
AND tmp.status = 'approved'
)
AND STATUS = 'rejected'
Why did you use left outer join ? I believe simple join will get you the result ..
SELECT u.id AS user_id,s.*, u.name
FROM submissions s
JOIN users u
ON s.user_id = u.id AND s.status = 'rejected'
OR
SELECT u.id AS user_id,s.*, u.name
FROM submissions s
JOIN users u
ON s.user_id = u.id
WHERE s.status = 'rejected'
create table users
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,name VARCHAR(12) NOT NULL UNIQUE
);
INSERT INTO users VALUES
(1,'John'),
(2,'Doe'),
(3,'Testing'),
(4,'Sample');
create table submissions
(id int not null auto_increment primary key
,userid int not null
,title varchar(12) not null
,status varchar(12) not null
);
insert into submissions values
( 1,1,'title1','approved'),
( 2,1,'title2','rejected'),
( 3,2,'title3','approved'),
( 4,2,'title4','approved'),
( 5,3,'title5','rejected'),
( 6,3,'title6','rejected'),
( 7,3,'title7','rejected'),
( 8,4,'title8','approved'),
( 9,4,'title9','approved'),
(10,4,'title10','rejected'),
(11,4,'title11','rejected');
SELECT u.name
, x.*
FROM submissions x
JOIN users u
ON u.id = x.userid
LEFT
JOIN submissions y
ON y.status = 'approved'
AND y.userid = x.userid
WHERE x.status = 'rejected'
AND y.userid IS NULL;
+---------+----+--------+--------+----------+
| name | id | userid | title | status |
+---------+----+--------+--------+----------+
| Testing | 5 | 3 | title5 | rejected |
| Testing | 6 | 3 | title6 | rejected |
| Testing | 7 | 3 | title7 | rejected |
+---------+----+--------+--------+----------+
http://sqlfiddle.com/#!9/1e9da/1
You can use the NOT IN(your exclude criteria) format as below.
SELECT u.id AS user_id,s.*, u.name
FROM submissions s
LEFT OUTER JOIN users u
ON s.user_id = u.id
WHERE s.user_id NOT IN(
SELECT tmp.user_id
FROM submissions tmp
WHERE tmp.status = 'approved'
)
AND STATUS = 'rejected'

Determine friends / friend requested / not friends using MySQL query

I have this MySQL table:
Let's imagine I am logged in as user 1 and I'm browsing the profile of user 2. As we are mutual friends (1 is friend with 2 and 2 is friend with 1), I need to echo "friends."
When I browse the profile of user 4, whose friendship I have requested, but they haven't accepted yet, I need to echo "friend requested."
When browsing any other user, I need to echo "add friend."
I can do the PHP part, I just don't know how to do the MySQL query.
SELECT COUNT(*) as `count`, `user`
FROM `friends`
WHERE
(`user` = 1 AND `friend` = 16) OR
(`user` = 16 AND `friend` = 1)
When count is
2 = mutual (you are friend with that person)
1 and user is you - you requested that person friendship
1 and user is that person - you are being requested for friendship by that person
0 = no connection between you and that person
select (case
when subquery.a > 0 and subquery.b > 0 then 'friends'
when subquery.a > 0 then 'friend requested'
else 'add friend' end) as "friend_string"
from (
select
(select count(*) from relationships where user = '$my_user_id' and friend = '$opponent_user_id') as a,
(select count(*) from relationships where user = '$opponent_user_id' and friend = '$my_user_id') as b
) subquery
Please replace relationships with your table name, and $ variables with yours.
Are you expecting something like this? (just for the sql part, not the PHP :) )
SQLFIDDLE DEMO
SELECT DISTINCT a. user,
CASE
WHEN a.user = b.friend
AND a.friend = b.user THEN b.friend
ELSE ''
end friends,
CASE
WHEN a.user = b.friend
AND a.friend <> b.user THEN a.friend
ELSE ''
end friendreq,
CASE
WHEN a.user <> b.friend
AND a.friend <> b.user THEN a.friend
ELSE ''
end addfriend
FROM demo a
LEFT JOIN demo b
ON a.id > b.id;
| USER | FRIENDS | FRIENDREQ | ADDFRIEND |
------------------------------------------
| 1 | | | |
| 2 | 2 | | |
| 1 | | | 16 |
| 1 | | 16 | |
| 16 | | | |
| 16 | | | 1 |
| 16 | 16 | | |
| 1 | | | 4 |
| 1 | | 4 | |

Joining two tables without returning unwanted row

My table structure looks like this:
tbl.users tbl.issues
+--------+-----------+ +---------+------------+-----------+
| userid | real_name | | issueid | assignedid | creatorid |
+--------+-----------+ +---------+------------+-----------+
| 1 | test_1 | | 1 | 1 | 1 |
| 2 | test_2 | | 2 | 1 | 2 |
+--------+-----------+ +---------+------------+-----------+
Basically I want to write a query that will end in a results table looking like this:
(results table)
+---------+------------+---------------+-----------+--------------+
| issueid | assignedid | assigned_name | creatorid | creator_name |
+---------+------------+---------------+-----------+--------------+
| 1 | 1 | test_1 | 1 | test_1 |
| 2 | 1 | test_1 | 2 | test_2 |
+---------+------------+---------------+-----------+--------------+
My SQL looks like this at the moment:
SELECT
`issues`.`issueid`,
`issues`.`creatorid`,
`issues`.`assignedid`,
`users`.`real_name`
FROM `issues`
JOIN `users`
ON ( `users`.`userid` = `issues`.`creatorid` )
OR (`users`.`userid` = `issues`.`assignedid`)
ORDER BY `issueid` ASC
LIMIT 0 , 30
This returns something like this:
(results table)
+---------+------------+-----------+-----------+
| issueid | assignedid | creatorid | real_name |
+---------+------------+-----------+-----------+
| 1 | 1 | 1 | test_1 |
| 2 | 1 | 2 | test_1 |
| 2 | 1 | 2 | test_2 |
+---------+------------+-----------+-----------+
Can anyone help me get to the desired results table?
SELECT
IssueID,
AssignedID,
CreatorID,
AssignedUser.real_name AS AssignedName,
CreatorUser.real_name AS CreatorName
FROM Issues
LEFT JOIN Users AS AssignedUser
ON Issues.AssignedID = AssignedUser.UserID
LEFT JOIN Users AS CreatorUser
ON Issues.CreatorID = CreatorUser.UserID
ORDER BY `issueid` ASC
LIMIT 0, 30
On the general knowledge front, our illustrious site founder wrote a very nice blog article on this subject which I find myself referring to over and over again.
Visual Explanation of SQL Joins
Use this:
SELECT
`issues`.`issueid`,
`issues`.`creatorid`,
`creator`.`real_name`,
`issues`.`assignedid`,
`assigned`.`real_name`
FROM `issues` i
INNER JOIN `users` creator ON ( `creator`.`userid` = `issues`.`creatorid` )
INNER JOIN `users` assigned ON (`assigned`.`userid` = `issues`.`assignedid`)
ORDER BY `issueid` ASC
LIMIT 0 , 30
SELECT DISTINCT (i.issueid, i.creatorid, i.assignedid, u.real_name)
FROM issues i, users u
WHERE u.userid = i.creatorid OR u.userid = assignedid
ORDER BY i.issueid ASC
LIMIT 0 , 30
Not sure if the parenthesis are needed or not.
Does this work?
SELECT
i.issueid,
i.assignedid,
u1.real_name as assigned_name,
i.creatorid,
u2.real_name as creator_name
FROM users u1
INNER JOIN issues i ON u1.userid = i.assignedid
INNER JOIN users u2 ON u2.userid = i.creatorid
ORDER BY i.issueid
SELECT
i.issueid,
i.assignedid,
a.real_name,
i.creatorid,
c.real_name
FROM
issues i
INNER JOIN users c
ON c.userid = i.creatorid
INNER JOIN users a
ON a.userid = i.assignedid
ORDER BY
i.issueid ASC