MySQL find ids for which there is no existing row - mysql

I have three tables :
1. Person (person_id, name) : (1, "Test1"), (2, "Test2"), (3, "Test3")
2. Role (role_id, description) : (1, "Admin"), (2, "Designer"), (3, "Developer") ..
3. PersonRoles (person_id, role_id) : (1 , 1), (1, 2), (2, 3), (2, 1), (3, 1)
Is it possible in MySQL with a query to get the ids of the people for which there`s no row with exact role in the PersonRoles table. For Example if I want to check for "Designer" role the query should return ids: 2 and 3

Here is your solution:
select person_id from Person
where person_id not in
(select person_id from Role r
inner join PersonRoles pr on pr.role_id=r.role_id
where r.description='Designer')

You could use the not exists operator:
SELECT *
FROM Person p
WHERE NOT EXISTS (SELECT *
FROM Role r
JOIN PersonRoles pr ON r.role_id = pr.role_id
WHERE r.description = 'Designer' AND
pr.person_id = p.person_id)

Related

SQL query to return users with no active group memberships

I've got this schema, which has the ability to soft-delete/archive a group:
create table users (id int, name varchar(255));
create table groups (id int, name varchar(255), archived_at datetime);
create table memberships (group_id int, user_id int);
insert into users (id, name) values
(1, 'no groups'),
(2, 'only active groups'),
(3, 'only archived groups'),
(4, 'active and archived groups');
insert into groups (id, name, archived_at) values
(1, 'active group', null),
(2, 'archived group', '2021-04-13');
insert into memberships (user_id, group_id) values
(2, 1),
(3, 2),
(4, 1),
(4, 2);
And I want to query users that have "no groups": exactly zero Active (non-archived) groups, plus zero or more Archived groups.
-- How do I get this to return only users 1 and 3?
select *
from users u
left join memberships m on m.user_id = u.id
left join groups g on g.id = m.group_id
where
m.group_id is null
or
g.archived_at is not null
Is this possible with the current schema?
https://www.db-fiddle.com/f/h1szvQmQHp1g84Ex9WvRHy/1
This sounds like not exists:
select u.*
from users u
where not exists (select 1
from memberships m join
groups g
on m.group_id = g.id
where m.user_id = u.id and g.archived_at is null
);
Presumably, an active group is one that has not been archived.

PL/SQL SELECT on two tables

I've got two tables. One of these contains userData and the other one contains userGroups linked to users which are in a Group.
Means like a user can be in 0 or more Groups.
I'm trying to get this columns with a query used like this:
SELECT distinct(a.userID), a.userName, Count(b.userID_FID)
FORM userData a,
userGroup b
WHERE a.userID = b.userID_FID
But somehow the Count part returning me a wrong number.
SELECT max(a.userName), Count(distinct b.userID_FID)
FROM userData a
JOIN userGroup b
ON a.userID = b.userID_FID
GROUP BY a.userID
My suggestion is do not use many tables with from
This will get you the number of groups each user is in.
To build the test scenario, use this:
create table #groups
(userID_FID int
,groups int
);
create table #user
(userID int);
insert into #groups values (1, 5);
insert into #groups values (1, 2);
insert into #groups values (1, 3);
insert into #groups values (1, 4);
insert into #groups values (2, 3);
insert into #groups values (2, 4);
insert into #groups values (3, 1);
insert into #groups values (3, 5);
insert into #user values (1);
insert into #user values (2);
insert into #user values (3);
select * from #groups;
select * from #user;
This piece is the query you want, according to the test scenario above:
select userid, count(*) as NumberOfGroups
from #groups g
, #user u
where g.userID_FID = u.userID
group by userID;

SQL: Sort on number of row entries and join

How do I find duplicate entries in a column (like multiple rows in student_id column) and join those to another table with common id (like student_id from another table to get student_name)?
grades:
student_id,grade
6,A
1,B
1,F
7,C
6,A
students:
student_id,student_name
1,roy
6,bob
7,art
so that I can get a joined table of only duplicate student rows on student_id that resembles:
student_id,student_name,grade
6,bob,A
1,roy,B
1,roy,F
6,bob,A
It would be nice to then just return the names of the most recent data frame:
bob
roy
Try this:
select students.student_name
from grades
inner join students on grades.student_id = students.student_id
group by student_name
having count(*) > 1
Example:
create table grades (student_id int, grade char(1));
insert into grades values (6, 'A'), (1, 'B'), (1, 'F'), (7, 'C'), (6, 'A');
create table students (student_id int, student_name varchar(20));
insert into students values (1, 'roy'), (6,'bob'), (7,'art');
select students.student_name
from grades
inner join students on grades.student_id = students.student_id
group by student_name
having count(*) > 1
Result:
student_name
bob
roy
This should result in bob and roy.
Command line on Linux, assuming you have sqlite:
~$ sqlite
SQLite version 2.8.17
Enter ".help" for instructions
sqlite> create table grades (student_id int, grade char(1));
sqlite> insert into grades values (6, 'A');
sqlite> insert into grades values (1, 'B');
sqlite> insert into grades values (1, 'F');
sqlite> insert into grades values (7, 'C');
sqlite> insert into grades values (6, 'A');
sqlite>
sqlite> create table students (student_id int, student_name varchar(20));
sqlite> insert into students values (1, 'roy');
sqlite> insert into students values (6,'bob');
sqlite> insert into students values (7,'art');
sqlite>
sqlite> select students.student_name
...> from grades
...> inner join students on grades.student_id = students.student_id
...> group by student_name
...> having count(*) > 1;
roy
bob
sqlite>
Find duplicates and then join it back for more information:
SELECT s.student_id, s.student_name
FROM student s
INNER JOIN
(SELECT g.student_id
FROM grades g
GROUP BY g.student_id
HAVING COUNT(*) > 1) dups
ON s.student_id = dups.student_id

Help with INSERT INTO..SELECT

I'm inserting a large number of rows into Table_A. Table_A includes a B_ID column which points to Table_B.B_ID.
Table B has just two columns: Table_B.B_ID (the primary key) and Table_B.Name.
I know the value for every Table_A field I'm inserting except B_ID. I only know the corresponding Table_B.Name. So how can I insert multiple rows into Table_A?
Here's a pseudocode version of what I want to do:
REPLACE INTO Table_A (Table_A.A_ID, Table_A.Field, Table_A.B_ID) VALUES
(1, 'foo', [SELECT B_ID FROM Table_B WHERE Table_B.Name = 'A'),
(2, 'bar', [SELECT B_ID FROM Table_B WHERE Table_B.Name = 'B'),...etc
I've had to do things like this when deploying scripts to a production environment where Ids differed in environments. Otherwise it's probably easier to type out the ID's
REPLACE INTO table_a (table_a.a_id, table_a.field, table_a.b_id)
SELECT 1, 'foo', b_id, FROM table_b WHERE name = 'A'
UNION ALL SELECT 2, 'bar', b_id, FROM table_b WHERE name = 'B'
If the values:
(1, 'foo', 'A'),
(2, 'bar', 'B'),
come from a (SELECT ...)
you can use this:
INSERT INTO Table_A
( A_ID, Fld, B_ID)
SELECT Data.A_ID
, Data.Field
, Table_B.B_ID
FROM (SELECT ...) As Data
JOIN Table_B
ON Table_B.Name = Data.Name
If not, you can insert them into a temporary table and then use the above, replacing (SELECT ...) with TemporaryTable.
CREATE TABLE HelpTable
( A_ID int
, Fld varchar(200)
, Name varchar(200)
) ;
INSERT INTO HelpTable
VALUES
(1, 'foo', 'A'),
(2, 'bar', 'B'), etc...
;
INSERT INTO Table_A
( A_ID, Field, B_ID)
SELECT HelpTable.A_ID
, HelpTable.Fld
, Table_B.B_ID
FROM HelpTable
JOIN Table_B
ON Table_B.Name = HelpTable.Name
;
DROP TABLE HelpTable ;

Find rows that has ALL the linked rows

I've got two tables:
User (id, name, etc)
UserRight (user_id, right_id)
I want to find the users who have rights 1, 2 and 3, but no users who only have one or two of these. Also, the number of rights will vary, so searches for (1,2,3) and (1,2,3,4,5,6,7) should work with much the same query.
Essentially:
SELECT *
FROM User
WHERE (
SELECT right_id
FROM tblUserRight
WHERE user_id = id
ORDER BY user_id ASC
) = (1,2,3)
Is this possible in MySQL?
SELECT u.id, u.name ...
FROM User u
JOIN UserRight r on u.id = r.user_id
WHERE right_id IN (1,2,3)
GROUP BY u.id, u.name ...
HAVING COUNT DISTINCT(right_id) = 3
You can also do this using PIVOT, especially if you want a visual representation. I did this on SQL Server - you may be able to translate it.
Declare #User Table (id Int, name Varchar (10))
Declare #UserRight Table (user_id Int, right_id Int)
Insert Into #User Values (1, 'Adam')
Insert Into #User Values (2, 'Bono')
Insert Into #User Values (3, 'Cher')
Insert Into #UserRight Values (1, 1)
Insert Into #UserRight Values (1, 2)
Insert Into #UserRight Values (1, 3)
--Insert Into #UserRight Values (2, 1)
Insert Into #UserRight Values (2, 2)
Insert Into #UserRight Values (2, 3)
Insert Into #UserRight Values (3, 1)
Insert Into #UserRight Values (3, 2)
--Insert Into #UserRight Values (3, 3)
SELECT *
FROM #User U
INNER JOIN #UserRight UR
ON U.id = UR.User_Id
PIVOT
(
SUM (User_Id)
FOR Right_Id IN ([1], [2], [3])
) as xx
WHERE 1=1
SELECT *
FROM #User U
INNER JOIN #UserRight UR
ON U.id = UR.User_Id
PIVOT
(
SUM (User_Id)
FOR Right_Id IN ([1], [2], [3])
) as xx
WHERE 1=1
AND [1] IS NOT NULL
AND [2] IS NOT NULL
AND [3] IS NOT NULL
In correspondance with the errors in my answer pointed out, here a solution with count and a subquery:
SELECT *
FROM User
WHERE 3 = (
SELECT Count(user_id)
FROM tblUserRight
WHERE right_id IN (1,2,3)
AND user_id = User.id
)
An optimizer may of course change this to Martin Smith's solution (i.e. by using a group by).