create mysql view for many-to-many relationship - mysql

MYSQL script to create a MYSQL view Table to show use id , email , and the assigned role/s name/s through many-to-many relationship
I wrote the script and joined tables to create the view table but it shows role name for the first record only the other users has null although they have roles!.
Tables
users (id, email)
roles (id, name)
role_users (id, user_id, role_id)
My script
DROP VIEW IF EXISTS users_view;
CREATE VIEW users_view AS
SELECT users.id AS user_id, users.email, roles_names.role_name
FROM users
LEFT JOIN(
SELECT role_users.user_id AS roles_user_id,
role_users.role_id AS roles_role_id
FROM role_users
) AS user_roles ON (roles_user_id = users.id)
LEFT JOIN(
SELECT roles.id AS roleid, roles.name AS role_name
FROM roles
) AS roles_names ON (roleid = user_roles.roles_user_id)
view created success but it shows the role name for the first user only , other users has role_name value of null although they have records at role_users table

SELECT u.id AS user_id, u.email, IFNULL(u_roles.role_name,'') as role_name
FROM users u
LEFT JOIN
(SELECT GROUP_CONCAT(DISTINCT r.name SEPARATOR ',') as role_name, ur.user_id
FROM roles r INNER JOIN user_roles ur
ON r.id = ur.role_id
GROUP BY ur.user_id) u_roles
ON u.id = u_roles.user_id
Since role users will be having all only roles from roles table, inner join on roles and user_roles will be fine.
By this query all records which are having an entry in user_roles table will show role name and rest of the users will be having null as role name (adding IFNULL check to make null values an empty string)

Related

sql - A query to retrieve information from two tables depending on multiple where clauses

A query to get a list of all crimes with reason and the name of both the criminal and the police, that's what I am trying to achieve.
This is what I've tried:
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, users.name
FROM `users`
INNER JOIN `crimes` on users.id = crimes.user_criminal_id
That returns only the name of the criminal...
Table structure
CREATE TABLE users (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(24)
);
INSERT INTO users (name) VALUES ("Divergent");
INSERT INTO users (name) VALUES ("SomeGuy");
CREATE TABLE crimes (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
user_criminal_id INT(11),
user_police_id INT(11),
reason VARCHAR(256)
);
INSERT INTO crimes (user_criminal_id, user_police_id, reason) VALUES (2, 1, "Corruption");
This works, but it gets only the police name
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, c.name, p.name
FROM crimes
JOIN users c on c.id = crimes.user_criminal_id
JOIN users p on p.id = crimes.user_police_id
Join a with users a second time to get the police name.
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, u1.name AS criminal u2.name AS police
FROM `users` AS u1
INNER JOIN `crimes` on u1.id = crimes.user_criminal_id
INNER JOIN `users` AS u2 ON u2.id = crimes.user_police_id
You need to double join. I prefer to query crimes so you'll understand why :
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, c.name, p.name
FROM crimes
JOIN users c on users.id = crimes.user_criminal_id
JOIN users p on users.id = crimes.user_police_id

Select only one row from a join based on foreign condition

The main idea
user hasMany roles (data is stored in the tables: users, roles, user_role)
i want to know if the user is admin OR client
this data will then be joined to a different result
What I'm doing
SELECT
`users`.`id`,
`users`.`name`,
`roles`.`display_name`
FROM `users`
JOIN role_user ON users.id = role_user.user_id
JOIN roles ON role_user.role_id = roles.id
Why it's wrong
Because this is the result I get
id Name Display name
1 admin Admin
1 admin Client
2 admin2 Admin
2 admin2 Client
3 client Client
7 test Admin
7 test Client
What I want
id Name Display name
1 admin Admin
2 admin2 Admin
3 client Client
7 test Admin
How I'm working to make this work
Using aggregates somehow
Using cases somehow
Joining with a subset of the data somehow
Thank you for your idea !
[Update] Here is an sqlFiddle describing the issue.
SELECT
`users`.`id`,
`users`.`name`,
`roles`.`display_name`
FROM `users`
LEFT JOIN role_user ON users.id = role_user.user_id
left JOIN roles ON role_user.role_id = roles.id
This solution should work fine for you:
select * from users
where users.id in (
select users.id FROM users
JOIN role_user ON (users.id = role_user.user_id)
JOIN roles ON (role_user.role_id = roles.id)
where roles.display_name like 'admin'
)
Union
select * from users
where users.id not in (
select users.id FROM users
JOIN role_user ON (users.id = role_user.user_id)
JOIN roles ON (role_user.role_id = roles.id)
where roles.display_name like 'admin'
);
Fiddle here: http://sqlfiddle.com/#!9/0fabba/42
I would recommend to create a view for the sub-query, but I couldn't do it on fiddle.
Example using a view:
Create or replace view admin_list as
select users.id FROM users
JOIN role_user ON (users.id = role_user.user_id)
JOIN roles ON (role_user.role_id = roles.id)
where roles.display_name like 'admin';
Then the query will be much compact:
select * from users
where users.id in (
select users.id FROM admin_list
)
Union
select * from users
where users.id not in (
select users.id FROM admin_list
);
My own solution was the following
create a view to get the user's role id (either admin or client)
join with the view
Specifically here is the view
CREATE VIEW vw_users_role_by_priority AS (
SELECT
role_user.user_id,
min(role_user.role_id) AS 'role_id'
FROM role_user
JOIN roles ON role_user.role_id = roles.id
GROUP BY role_user.user_id
);
And here is the final query
SELECT
`users`.`id`,
`roles`.display_name
FROM `users`
JOIN vw_users_role_by_priority ON vw_users_role_by_priority.user_id = users.id
JOIN roles ON roles.id = vw_users_role_by_priority.role_id
ORDER BY `users`.`name` ASC;
Try with this query :
SELECT users.`id`, users.`name`, MIN(roles.`display_name`)
FROM `users` users, `role_user` role_user, `roles` roles
WHERE users.id = role_user.user_id AND role_user.role_id = roles.id
GROUP BY users.`id`
In your query you join tables which give you all possible combinations of results. This query will select only the role_user/roles for each found userId.
EDIT : added "group by" to select only unique users and "min" to select its alphabetically lowest role (so "admin" get selected before "client")

MySQL - Get count of users that have a "user-contact"

Consider the three tables in the following Schema SQL Fiddle.
This mini-model represents a simple example of a database I'm working on .
The Users table has 4 attributes, the user_id (primary key auto_increment), the country_id which is a foreign key referencing the Country table, the user_msisdn which is the phone number of the user, and the username.
The Contacts table has 3 attributes, the contact_id (primary key auto_increment), the user_id which is a foreign key referencing the Users table, and the contact_msisdn which is the user contact's phone numbers (the phone numbers on your phone contacts list).
The relation between the Users table and the Contacts table is many-to-many, a user can have many contacts, and a contact can be found in any user's contacts list.
Requirements:
For each country, get the count of users that have at least one "user-contact", where a "user-contact" is a contact that is a user, and the count of users that don't have any "user-contact".
E.g., For country = 'Sweden' (country_id = 3), the user_id = 3 has two contacts in the Contacts table that are considered "user-contact" with (msisdn = '+220011223344' & '+224433221100'). So the query results that I want: Sweden has 1 user (user_id = 3) that has at least one "user-contact" and zero users that have no "user-contact", and so on for each country.
Try this:
SELECT c.country_id,
COUNT(DISTINCT CASE WHEN u2.user_id IS NOT NULL THEN u.user_id END) as has_contact_that_is_user ,
COUNT(distinct CASE WHEN u2.user_id is null AND u.user_id IS NOT NULL THEN u.user_id END) as has_no_contact_that_is_user
FROM Country c
LEFT JOIN users u
ON(c.country_id = u.country_id)
LEFT JOIN contacts co
ON(co.user_id = u.user_id)
LEFT JOIN Users u2
ON(u2.user_msisdn = co.contact_msisdn)
GROUP BY c.country_id
Fiddle

How to select multiple values with join in sql

I have a database called "forum" with the tables:
"users", containing: user_id, username, password
"posts", containing: post_id, user_id (FK from "users"), type, title, content
"comments", containing: comment_id, user_id (FK from "users"), post_id (FK from posts"), content
At the moment, i'm using the query:
SELECT *
FROM forum.comments
JOIN forum.posts ON (comments.post_id = posts.post_id)
JOIN forum.users ON (posts.user_id= users.user_id)
With this query i can only display the username of the person who made the post, not the comment. How can i change this to show who made the comment, AND who made the post in the same query?
(If someething in the query is written wrong, it's just cause i had to translate it from another language, the query does return results).
You can join the table more than once if you alias the tables:
SELECT * FROM forum.comments AS c
JOIN forum.posts AS p ON (comments.post_id = posts.post_id)
JOIN forum.users AS u1 ON (posts.user_id = users.user_id)
JOIN forum.users AS u2 ON (comments.user_id = users.user_id)
When you do that, you can reference columns from the table by doing something like SELECT u1.username, u2.username ....

A complex data not sure how to explain it

Hi I have a table called Users and a table called friends, friends table have two data types UserID and FriendID, (foreign key of both data types to primary key of the Users table),
I need to give an ID and find a list of that persons friends'name, I am not sure if I have designed the tables wrongly or I should rewrite the query.
my query is as following, (so far it just shows the details of first matched person)
SELECT Users.Name
FROM Users
WHERE Users.ID = SELECT Friends.UserID
FROM Friends,Users
WHERE Users.ID = (Select Users.ID
From Users
WHERE Users.Username = 'John')
Try this:
SELECT Users.Name FROM Users WHERE Users.ID IN -- Get names that belongt to ID's
(SELECT FriendID FROM Friends WHERE UserID = -- All ID's of the Friends of
(SELECT UserID FROM Users WHERE Name = 'John')) -- Johns ID
I've solved it by changing the first = to IN
is this you want to achieve ??
User
UserID (PK)
Name
Friend
FriendID (PK)
UserID (FK)
select User.Name from User u join Friend f on f.UserID = u.UserID where Name = 'John'