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'
Related
This is the situation I'm dealing with:
I have 4 tables:
Users table:
+----+-------+
| id | name |
+----+-------+
| 1 | name1 |
| 2 | name2 |
| 3 | name3 |
+----+-------+
Assignment table:
+----+-----------------+
| id | assignment_name |
+----+-----------------+
| 11 | name1 |
| 12 | name2 |
| 13 | name3 |
+----+-----------------+
Submissions:
+----+---------------+---------+
| id | assignment_id | user_id |
+----+---------------+---------+
| 1 | 11 | 3 |
| 2 | 12 | 1 |
| 3 | 11 | 2 |
+----+---------------+---------+
Group_submissions
+----+----------------+---------+
| id | submission_id | user_id |
+----+----------------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 1 |
+----+----------------+---------+
The submission table has an assignment_id to tell in which assignment the submission belongs to.
Also users can submit a group submission, where the one that does the submission goes to the submissions table, while the others go to the group_submissions table. That way it will be counted as one submission instead of being 2,3...N submission based on how many people where in the group.
How can i get the users that have submitted a submission or have participated in a group submission in a given assignment?
The result should return the user or users that have are in the submissions table or in the group_submissions table based on a assignment id
The result should look something likes this:
+----+-------+
| id | name |
+----+-------+
| 1 | name1 |
| 2 | name2 |
+----+-------+
It should basically return the user table.
This is what i have tried so far:
This only gives me the users that aren't in the submissions table but are in the group_submission
select * from users u
right join group_submissions gs on u.id = gs.student_id
right join assignment_submissions ass on gs.submission_id = ass.id
inner join assignments a on a.id = ass.assignment_id
where a.id = number
This only gives me the one user that made the submission (in the submissions table)
select * from users u
right join assignment_submissions ass on u.id= ass.student_id
right join group_submissions gs on ass.id = gs.submission_id
inner join assignments a on a.id = ass.assignment_id
where a.id = number
What should my join strategy be here? Or maybe joins are not the right option here.
NOTE: This is a MySQL database.
You could use exists:
select u.*
from users u
where
exists (
select 1
from submissions s
where s.user_id = u.id and s.assignment_id = ?
)
or exists (
select 1
from group_submissions gs
inner join submissions s on s.id = gs.submission_id
where gs.user_id = u.id and s.assignment_id = ?
)
I am thinking a union could do it. Like this
select * from
(
select u.*, assignment_id, 'assignment_submissions' as type from users u
inner join assignment_submissions ass on u.id= ass.student_id
union
select u.*, assignment_id, 'group_submissions' from users u
inner join group_submissions gs on u.id = gs.student_id
inner join assignments a on a.id = ass.assignment_id
)a
where assignment_id = ?
I have a table containing the following data:
+-----------------+--------------+----------------------+------------+--------------------+-----------+------+---------+---------+-----+------------------------+---------------------+
| notification_id | from_user_id | from_user_auth_level | to_user_id | to_user_auth_level | status_id | type | subject | message | url | timestamp_inserted_utc | timestamp_read_utc |
+-----------------+--------------+----------------------+------------+--------------------+-----------+------+---------+---------+-----+------------------------+---------------------+
| 1 | NULL | NULL | 1 | 1 | 0 | 2 | test | test | url | 2010-10-10 00:00:00 | 2011-10-10 00:00:00 |
| 2 | 2 | 5 | 1 | 1 | 0 | 2 | test | test | url | 2010-10-10 00:00:00 | 2011-10-10 00:00:00 |
| 3 | 3 | 5 | 1 | 1 | 0 | 2 | test | test | url | 2010-10-10 00:00:00 | 2011-10-10 00:00:00 |
| 4 | 2295 | 4 | 1 | 1 | 0 | 2 | test | test | url | 2010-10-10 00:00:00 | 2011-10-10 00:00:00 |
| 5 | 10 | 1 | 1 | 1 | 0 | 2 | test | test | url | 2010-10-10 00:00:00 | 2011-10-10 00:00:00 |
+-----------------+--------------+----------------------+------------+--------------------+-----------+------+---------+---------+-----+------------------------+---------------------+
And then I have some other tables like 'users', 'companies', 'organizations', ... etc.
I need to be able to get the username, gender and image of every notification (based on the from_user_id and from_user_auth_level).
But the problem resides in the fact, that this info resides in different places, depending on what the user_auth_level is.
For example: if my user is a "regular" user, his auth_level will be 1. And the image will reside in my "users" table, and the gender is applicable.
But if the user has auth_level == 5, it means he is an organization. In this case, gender is not applicable, and the image resides in the "organization" table, this needs to be linked via users to user_roles and then to the organization.
And this goes on for every user type, they all require different joins.
I have created a working query, but this uses UNION's everywhere, and I have read that it is not the best to use for performance reasons, so i'm hoping someone can guide me to improving this query with performance in mind:
SELECT n.*, NULL as username, NULL as gender, NULL as picture
FROM notification as n
WHERE n.from_user_auth_level IS NULL
AND n.to_user_id = $userid
UNION
SELECT n.*, u.username, u.gender as gender, u.profile_picture as picture
FROM notification as n
LEFT JOIN users AS u ON n.from_user_id = u.user_id
WHERE n.from_user_auth_level = 1
AND n.to_user_id = $userid
UNION
SELECT n.*, u.username, NULL as gender, c.logo as picture
FROM notification as n
LEFT JOIN users AS u ON n.from_user_id = u.user_id
LEFT JOIN user_companies AS uc on u.user_id = uc.user_id
LEFT JOIN company as c on uc.company_id = c.company_id
WHERE n.from_user_auth_level = 4
AND n.to_user_id = $userid
UNION
SELECT n.*, u.username, NULL as gender, o.logo as picture
FROM notification as n
LEFT JOIN users AS u ON n.from_user_id = u.user_id
LEFT JOIN user_roles as ur on u.user_id = ur.user_id
LEFT JOIN organization as o on ur.org_id = o.org_id
WHERE n.from_user_auth_level = 5
AND n.to_user_id = $userid
UNION
SELECT n.*, u.username, NULL as gender, o.logo as picture
FROM notification as n
LEFT JOIN users AS u ON n.from_user_id = u.user_id
LEFT JOIN user_roles as ur on u.user_id = ur.user_id
LEFT JOIN organization as o on ur.org_id = o.org_id
WHERE n.from_user_auth_level = 7
AND n.to_user_id = $userid
UNION
SELECT n.*, u.username, NULL as gender, NULL as picture
FROM notification as n
LEFT JOIN users AS u ON n.from_user_id = u.user_id
WHERE n.from_user_auth_level = 9
AND n.to_user_id = $userid"
After I get this result, I use PHP to order the results based on the timestamp_inserted_utc, since it's not possible to get the correct results with the UNION.
I'd use the notification table as base and used conditional outer joins as:
select
n.*,t1.gender, t2.orgNo
from
notifications n
left outer join table1 t1 on (n.auth=1 and more join)
left outer join table2 t2 on (n.auth=2 and more..)
You will have more columns but their name would make sense and you may merge at application level.
Suggested indexes (most are "covering"):
n: (from_user_auth_level, to_user_id, from_user_id)
u: (user_id, username, profile_picture, gender)
o: (org_id, logo)
ur: (org_id, user_id)
c: (company_id, logo)
uc: (company_id, user_id)
(They may even speed up Teson's Answer.)
I have sample data that looks like this
| users |
| user_id | email |
|---------|--------------------------|
| 1 | test#example.com |
| 2 | Kanchhi#example.com |
| 3 | modi#example.com |
| 4 | andy#example.com |
| 5 | maya#example.com |
| 6 | jetli#example.com |
| 7 | john#example.com |
| user_relations |
| user_relation_id | requestor_user_id | receiver_user_id | friend_status |
|------------------|-------------------|------------------|---------------|
| 1 | 2 | 4 | 1 |
| 2 | 2 | 6 | 1 |
| 3 | 2 | 7 | 1 |
| 4 | 5 | 2 | NULL |
| 5 | 5 | 7 | NULL |
| 6 | 7 | 2 | NULL |
| 7 | 7 | 4 | 1 |
| 8 | 7 | 5 | 1 |
| 9 | 7 | 6 | 1 |
| 10 | 4 | 2 | 1 |
| 11 | 4 | 3 | 1 |
| 12 | 4 | 5 | 1 |
| 13 | 4 | 6 | 1 |
| 14 | 4 | 7 | 1 |
If input is these two emails:
Kanchhi#example.com, john#example.com
Then my expected expected output is this (order does not matter):
andy#example.com
jetli#example.com
In the above example, the friends of user id 2 is user ids (4, 6, 7) and friend of user id 7 is user ids (2, 4, 5, 6). So the mutual friend of user id 2 and 7 is 4 and 6. I need email address of mutual user id.
Another example input is:
andy#example.com, john#example.com
Then expected output is this:
jetli#example.com
Kanchhi#example.com
maya#example.com`
In the above example, friend of user id 4 is (2, 6, 5, 3, 2) and friend of user id 7 is (6, 5, 2, 4). So mutual friend user id will be 2, 6, 5. I need email address of these user id in output.
Query 1- I tried but got wrong result:
SELECT u.email
FROM user_relations r
LEFT JOIN users u ON r.requestor_user_id = u.user_id
LEFT JOIN users z ON r.receiver_user_id = z.user_id
where u.email in ('Kanchhi#example.com','john#example.com') or
z.email in ('Kanchhi#example.com','john#example.com')
and r.friend_status = 1
group by u.email
having count(u.email ) > 1
Results- but not correc:
| email |
|---------------------|
| andy#example.com |
| john#example.com |
| Kanchhi#example.com |
How to get this?
The crux of the problem is building a list of users that are friends of Kanchi and John or have them as friends. Then count those users that appear in the list twice:
-- SELECT email FROM users WHERE userid IN (
SELECT friendid
FROM (
SELECT requestor_user_id AS userid, receiver_user_id AS friendid
FROM user_relations
WHERE friend_status = 1 AND requestor_user_id IN (
SELECT user_id
FROM users
WHERE email IN ('Kanchhi#example.com','john#example.com')
)
UNION ALL
SELECT receiver_user_id, requestor_user_id
FROM user_relations
WHERE friend_status = 1 AND receiver_user_id IN (
SELECT user_id
FROM users
WHERE email IN ('Kanchhi#example.com','john#example.com')
)
) AS X
GROUP BY friendid
HAVING COUNT(DISTINCT userid) = 2
Matching the results with users is trivial. For your sample input the result is:
4 Andy andy#example.com ashutosh 2019-01-11 13:34:05
6 jetli jetli#example.com ashutosh 2019-01-11 13:34:05
You can start from the user, then join the relations.
Then group on the receiver & requestor that are different from the user.
SELECT
case when u.user_id = r.receiver_user_id then requestor.email else receiver.email end as friend_email
FROM users u
JOIN user_relations r
ON (r.requestor_user_id = u.user_id OR r.receiver_user_id = u.user_id)
AND r.friend_status = 1
LEFT JOIN users requestor ON requestor.user_id = r.requestor_user_id
LEFT JOIN users receiver ON receiver.user_id = r.receiver_user_id
WHERE u.email in ('Kanchhi#example.com','john#example.com')
GROUP BY friend_email
HAVING COUNT(DISTINCT u.user_id) > 1
Result:
friend_email
----------------
andy#example.com
jetli#example.com
You could use this and use index in join coulmn for performance
SELECT DISTINCT u.email
FROM
(
SELECT r.receiver_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.requestor_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'Kanchhi#example.com'
UNION ALL
SELECT r.requestor_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.receiver_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'Kanchhi#example.com'
) k
INNER JOIN
(
SELECT r.receiver_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.requestor_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'john#example.com'
UNION ALL
SELECT r.requestor_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.receiver_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'john#example.com'
) j
ON k.id = j.id
INNER JOIN users u
ON k.id = u.user_id
AND u.email NOT IN ('Kanchhi#example.com','john#example.com');
Try this
SELECT usu.email from users usu where usu.user_id in (
select r.receiver_user_id
from sov.user_relations r
where r.requestor_user_id in (
SELECT u.user_id from users u where u.email in ('Kanchhi#example.com','john#example.com')
)
and r.friend_status = 1
group by r.receiver_user_id
having count(r.receiver_user_id) > 1
);
you can create extra function like below code
BEGIN
DECLARE limitCount INT DEFAULT 0;
DECLARE counter INT DEFAULT 0;
DECLARE res INT DEFAULT 0;
DECLARE temp TEXT;
SET limitCount = 1 + LENGTH(inputList) - LENGTH(REPLACE(inputList, ',',''));
simple_loop:LOOP
SET counter = counter + 1;
SET temp = SUBSTRING_INDEX(SUBSTRING_INDEX(inputList,',',counter),',',-1);
SET res = FIND_IN_SET(temp,targetList);
IF res > 0 THEN LEAVE simple_loop; END IF;
IF counter = limitCount THEN LEAVE simple_loop; END IF;
END LOOP simple_loop;
RETURN res;
END
AND USE THIS FUNCTION LIKE THIS
find_in_set_extra('Kanchhi#example.com, john#example.com','john#example.com') OR WHAT EVER YOUR INPUTS AND OUTPUTS.
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)
I have small trouble creating a query. I have two tables:
user_data
+----+---------+--------+
| id | mail | etc... |
+----+---------+--------+
| 1 | 1#m.com | ... |
| 2 | 2#m.com | ... |
| 3 | 3#m.com | ... |
+----+---------+--------+
contracts
+----+---------+--------+
| id | user_id | etc... |
+----+---------+--------+
| 1 | 1 | ... |
| 2 | 2 | ... |
| 3 | 1 | ... |
| 4 | 1 | ... |
| 5 | 3 | ... |
+----+---------+--------+
As you can see, the first table contains data about users and the secound one about their contracts. There will be always only one entry about a user, but a user can have multiple contracts. Now I need to find out
all users, theirs first contract id ( with the lowest id in contracts table ) and their email, if it's in the were parameters.
So far I have such query:
SELECT
u.id as user_id,
c.id as first_contract_id,
u.mail as email
FROM
user_data u
JOIN
contracts c ON u.id = c.user_id
WHERE
u.mail
IN (
'1#me.com',
'2#me.com',
'3#me.com'
);
Now I have no idea how I can select only the lowest contract ID from these results. Help apreciated.
SELECT
u.id as user_id,
min(c.id) as first_contract_id,
u.Mail as email
FROM
user_data u
JOIN
contracts c ON u.id = c.user_id
WHERE
u.mail IN ('1#me.com', '2#me.com', '3#me.com')
GROUP BY u.id
If you group by the user you can get the lowest contract by using min.
(And MySQL has no problem with selecting column that are not in a group)
select
u.id as user_id,
c.id as first_contract_id,
u.Mail as email
from users as u inner join
(
select min(id) as id,user_id from contracts
group by user_id
) as c
on u.id = c.user_id
WHERE
u.mail
IN (
'1#me.com',
'2#me.com',
'3#me.com'
);