SQL query with JOIN, WHERE, GROUPBY, COUNT - mysql

Below is an example of the schema I am working with.
How can I return the id and name of all users who have a permission, but DO NOT have a matching certification for that permission?
For example, the query would return 0, john in this case, since john has the "delete" permission, but does not have a matching "delete" certification.
(This schema is arbitrary and contrived, I'm just trying to get at the syntax/select logic for this query)
users
=====
id name
--------
0 john
1 joe
user_permissions
================
uid permission
--------------
0 'edit'
0 'delete'
1 'edit
user_certs
==========
uid cid
-------
0 'edit'
1 'edit'
I've tried, this, and I know the last line is wrong.
SELECT DISTINCT id, name FROM users
LEFT JOIN user_permissions users ON users.uid = user_permissions.uid
LEFT JOIN user_certs ON users.id = user_certs.uid
WHERE (user.permission = 'delete')
GROUP BY id, name
HAVING (COUNT(user_certs.cid = 'delete') = 0)

Get all the permissions that doesn't have a matching certificate, then group on the user:
select
u.id,
u.name
from
users u
inner join user_permissions p on p.uid = u.id
left join user_certs c on c.uid = p.uid and c.cid = p.permission
where
c.uid is null
group by
u.id,
u.name

Bud you are right.
The alias user dies not exist.

Related

Join to a table that its name is in a row result

I have tables called comment, admin, user.
comment:
body userType userId
--------------------------------------------
comment1 admin 1
comment2 user 2
admin:
id title
-----------------------
1 amir
2 farhad
user:
id title
-----------------------
1 kazem
2 ali
I want to get the comment and userTypes' title from it's table by one query like:
SELECT u.title, c.body FROM comment c, c.userType u
but it doesn't work!
A couple options come to mind:
using a union:
SELECT title, body FROM comment c JOIN user u on c.userId = u.id and userType = 'user'
UNION
SELECT title, body FROM comment c JOIN admin u on c.userId = u.id and userType = 'admin'
using left joins:
SELECT
COALESCE(u.title, a.title) as title,
c.body
FROM comment c
LEFT JOIN user u on c.userId = u.id and userType = 'user'
LEFT JOIN user a on a.userId = u.id and userType = 'admin'
WHERE COALESCE(u.title, a.title) is not null;
This uses ANSI SQL COALESCE. If you are using T-SQL (MS SQL), you will need to use ISNULL instead.
More generically, this is a case of the "Concrete Table Inheritance" pattern, and the userType column is called a discriminator.

Return multiple data with condition from multiple tables in mysql

I am having problem with fetching data from multiple tables with some conditions in MySQL.
I have follwing three tables:
Like Table
Like_id photoID userID
1 1 1
2 2 2
3 2 1
BookMark Table
bookmark_id photoID userID
1 1 1
2 2 2
3 2 1
Users Table
User_id Name Email
1 Max B maxb#gmailcom
2 Tom Smith toms#gmailcom
CONDITIONS:
At first i want to check whether there is any data from the LIKE table for the userID = 2. If there is no data it should return "false" otherwise it should return "true".
Similarly, i want to check whether there is any data from the BOOKMARK table for the userID = 2. If there is no data it should return "false" otherwise it should return "true".
Finally, i want to fetch the Name and Email from the USERS table for the userID = 2.
WANTED:
I want to achieve all these information in a SINGLE QUERY with the above mentioned conditions from these three tables.
SO FAR tried with this QUERY:
select Like.Like_id from (Like left join Users on Like.userID = Users.User_id)
left join BookMark on Users.User_id = BookMark.bookmark_id
where Users.User_id = 2
With #Gervs suggestion:
SELECT
u.user_id,
u.name,
u.email,
(CASE WHEN ISNULL(l.user_id) THEN 'false' ELSE 'true' END) AS 'likes',
(CASE WHEN ISNULL(b.user_id) THEN 'false' ELSE 'true' END) AS 'bookmarks'
FROM
users u
LEFT JOIN
likes l
ON u.user_id = l.user_id
LEFT JOIN
bookmarks
ON u.user_id = b.user_id
WHERE u.user_id = 2
GROUP BY u.user_id
What will be the easiest but efficient single query to fetch these information?
Will VIEW be a best option for these conditions?
Advanced thanks for your participation.
You can inner join both like table and bookmark table on users table, that is if you want only users that have entries in both tables.
SELECT
u.user_id,
u.name,
u.email,
COUNT(l.user_id) likes,
COUNT(b.user_id) bookmarks
FROM
users u
JOIN
likes l
ON u.user_id = l.user_id
JOIN
bookmarks b
ON u.user_id = b.user_id
WHERE u.user_id = 2
GROUP BY u.user_id
If you always want the user, just change the inner joins into left joins and likes and/or bookmarks will be zero if no entries are found
SELECT
u.user_id,
u.name,
u.email,
CASE WHEN COUNT(l.user_id) > 0 THEN 'true' ELSE 'false' END likes,
CASE WHEN COUNT(b.user_id) > 0 THEN 'true' ELSE 'false' END bookmarks
FROM
users u
LEFT JOIN
likes l
ON u.user_id = l.user_id
LEFT JOIN
bookmarks b
ON u.user_id = b.user_id
WHERE u.user_id = 2
GROUP BY u.user_id

Check if joined result set contains some value in SQL

I have a database structure similar to this:
asset
+----+---------+
| id | user_id |
+----+---------+
user_favorite
+----------+---------+
| asset_id | user_id |
+----------+---------+
I am looking to create a query where I can return all assets belonging to a given user AND a boolean indicating whether or not it is a "favorite" for them.
I can do this, where a count() equalling zero would mean it's not a favorite (but it seemed hacky and inefficient):
select distinct(a.asset_id),
(select count(*)
from user_favorite f
where f.user_id = MY USER ID
and f.asset_id = a.asset_id)
from asset a
left join user_favorite u on a.asset_id=u.asset_id
where a.user_id = MY USER ID;
I tried this (but it yielded multiple entries from assets when multiple users had favorited them:
select distinct (a.asset_id),
(u.user_id in (MY USER ID))
from asset a
left join user_favorite u on a.asset_id=u.asset_id
where a.user_id = MY USER ID;
I also tried this (but the IN condition wasn't respected):
select distinct(a.asset_id),
(u.user_id in (MY USER ID))
from asset a
left join user_favorite u on a.asset_id=u.asset_id
where a.user_id = MY USER ID group by u.user_id;
Is there some good way to do this query?
This is how I'd do it, but I'm sure there are many acceptable ways:
SELECT DISTINCT a.asset_id
,CASE WHEN u.asset_id IS NULL
THEN 0
ELSE 1
END AS IsFavourite
FROM asset a
LEFT JOIN user_favourite u ON a.asset_id = u.asset_id
AND a.user_id = u.user_id
WHERE a.userid = MY_USER_ID
LEFT JOIN favourites to assets, and if there is no favourite record present (u.asset_id IS NULL) then it is not a favourite, otherwise it is.
Maybe this helps a little
select
assetid,
case when isnull(fav.user_id,0) =1 Then 0 else 1 end
form
asset a
left outer join
user_favourite fav
on a.user_id = fav.user_id
where
fav.id = 'foobar'
GROUP BY assetid, fav.user_id

Mysql join from multiple tables

I have 3 tables
friends
posts
members
friends
========
id, to, from (user_id's), status
there are 3 status's -1 = denied, 0 = no response/new, 1 = accepted
posts
=======
p_id, user_id (same as member_id), text
members
========
member_id, f_name, l_name
If like to select the text from the post in 'posts' combine it with the users name from 'members' and only display posts where the user_id is in the 'friends' table.
I would like to know if it can be done, I've tried an IN () statement in my query which worked, but it creates a new problem with generating the csv inside the IN (). I'd perfer to do this through mysql, but if it can't be done I may use a global variable to store friend data (but then it will not be upto date or will have to be refreshed when a user gets a new friend).
As I understand it, you want to find the name and posts of all your friends, not any friend that's in the friend table at all...?
Your own user id being in $myId, this should do it (newest posts first);
EDIT: Added status check for friends
SELECT m.f_name, m.l_name, p.`text`
FROM members m
JOIN posts p
ON m.member_id = p.user_id
JOIN friends f
ON f.`to` = m.member_id OR f.`from` = m.member_id
WHERE (f.`from` = $myId OR f.`to`= $myId)
AND f.`status` = 1 AND m.member_id <> $myId
ORDER BY p.p_id DESC
Try this :
SELECT p.text,m.f_name,m.l_name FROM posts p
LEFT OUTER JOIN members m ON p.user_id=m.member_id
where p.user_id in(select id from friends);
OR
SELECT p.text,m.f_name,m.l_name FROM posts p
LEFT OUTER JOIN members m ON p.user_id=m.member_id
INNER JOIN friends f on p.user_id=f.id
If I understand correctly, you have a user_id and you want all the posts authored by "friends" of that user. This query starts at posts, joins that to friends (where the author is the "destination" of the friendship) (at which point the WHERE clause will filter out any non-friend posts), and then joins in members to fill out the author's name info.
SELECT
posts.p_id
posts.text,
CONCAT(members.f_name, " ", members.l_name)
FROM
posts
JOIN friends ON posts.user_id = friends.to
JOIN members ON posts.user_id = members.member_id
WHERE
friends.from = ?
GROUP BY posts.p_id
I added a subquery to get all your friends since I assumed that if you have these records
Friends
==================================
ID TO FROM STATUS
==================================
1 1 2 1
2 3 1 1
and your member_id = 1, your friends are 2, 3. right?
SELECT b.f_name,
b.L_name,
c.`text`
FROM
(
SELECT `to` friendID
FROM friends
WHERE `status` = 1 AND
`from` = ? -- change this to your ID
UNION
SELECT `from` friendID
FROM friends
WHERE `status` = 1 AND
`to` = ? -- change this to your ID
) a INNER JOIN members b
ON a.friendID = b.member_ID
LEFT JOIN posts c
ON a.friendID = c.user_id

Select the users are not friend of a specific user

Hi, I have these two tables: users and friends (friend_status = 1 means the request is sent, friend_status = 2 means they are friends). Now I want to select all users are not friend of a specific user. How to do?
Assuming the current user is 1. I tried this SQL. It works but it's too long and slow. The first selects all users sent request to user1 but not accepted. The second selects all users receive request from user1. The third and the fourth selects all users is not in "friends" table.
SELECT user_id, name, email
FROM
(
SELECT user_id, name, email
FROM users u INNER JOIN friends f ON u.user_id = f.sender
WHERE f.receiver = 1 AND friend_status <> 2
UNION
SELECT user_id, name, email
FROM users u INNER JOIN friends f ON u.user_id = f.receiver
WHERE f.sender = 1 AND friend_status <> 2
UNION
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.sender
WHERE f.receiver IS NULL
GROUP BY user_id
UNION
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.receiver
WHERE f.sender IS NULL
GROUP BY user_id
) T
GROUP BY user_id
Update: Add a pic.
SELECT
a.user_id,
a.name,
a.email,
b.status IS NOT NULL AS friend_status
FROM
users a
LEFT JOIN
friends b ON
a.user_id IN (b.sender, b.receiver) AND
1 IN (b.sender, b.receiver)
WHERE
(b.friend_id IS NULL OR b.status <> 2) AND
a.user_id <> 1
You had asked a question previously here - "Select users who aren't friends with anyone", and I provided an answer which utilized a LEFT JOIN.
Building off of that, to select users who aren't friends with a specific user, we just simply need to add that specific user's ID to the LEFT JOIN condition (1 IN (b.sender, b.receiver).
Minor Edit: Unless the user can friend him/herself, it wouldn't make sense to also select the user who we're selecting against!! So I added WHERE a.user_id <> 1.
Assuming you want to perform the query on user_id 1:
SELECT user_id, name, email
FROM users AS u
WHERE NOT EXISTS (SELECT * FROM friends AS f
WHERE (f.sender = u.user_id AND f.receiver = 1 AND f.friend_status = 2)
OR (f.sender = 1 AND f.receiver = u.user_id AND f.friend_status = 2)
)
AND u.user_id <> 1
The subquery fetches all the established friendship relationship in which user 1 is either the sender or the receiver. The outer query selects all users for which no such relationship exists. The user with ID 1 is excluded from the query using the last line, as, even if he cannot be friend with himself, I suppose that he should not appear in the final query result.
You may be able to simplify this by using something like this:
SELECT user_id, name, email
FROM
(
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.sender
WHERE IFNULL(friend_status,0) <> 2
GROUP BY user_id
UNION
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.receiver
WHERE IFNULL(friend_status,0) <> 2
GROUP BY user_id
) T
GROUP BY user_id
The IFNULL function returns the value of the first parameter, replacing NULLs with the value of the value second parameter. In this case it means that friend_status will be treated as 0 if there is no matching friend in the friends table, which allows you to reduce the number of selects in the UNION by half.
Try this query
select
u.user_id,
u.name,
u.email,
ifnull(f.friend_status,0) as Relation
from users as u
left join friends as f
on f.sender = u.user_id
where u.user_id not in(select
sender
from friends
where sender = 1)
Here sender = 1 means the user id = 1. You can pass user id to restrict this condition. Also status 0 means he is not friend. and 1 , 2 , 3 are according to your rules