Count the occurrences of how many events user hasn't responded - mysql

Taking into account the tables provided below, I want to be able to count the occurrences of how many Events a User still hasn't provided availability yet.
Table users
| name | id |
|---------|----|
| John | 1 |
| Francis | 2 |
| Peter | 3 |
| Mike | 4 |
Table events
| id | name |
|----|---------|
| 1 | Event 1 |
| 2 | Event 2 |
Table availability
| answer | event_id | user_id |
|--------|----------|---------|
| yes | 1 | 1 |
| yes | 2 | 1 |
| no | 1 | 2 |
| maybe | 2 | 4 |
The expected answer would then be:
Expected answer:
| user_id | count |
|---------|-------|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
| 4 | 1 |
I have been able to do a query for how many events each member has responded, but looking for how many hasn't responded.
DBFiddle: https://www.db-fiddle.com/f/mwv4F1wYZ9UxB56L9mMjFQ/1
SQL:
create table events (
id int auto_increment primary key,
name varchar(100) null
);
create table users (
name varchar(100) null,
id int auto_increment primary key
);
create table availability (
answer text null,
event_id int null,
user_id int not null,
constraint availability_events_id_fk
foreign key (event_id) references events (id),
constraint availability_users_id_fk
foreign key (user_id) references users (id)
);
INSERT INTO events (id, name) VALUES (1, 'Event 1');
INSERT INTO events (id, name) VALUES (2, 'Event 2');
INSERT INTO users (name, id) VALUES ('John', 1);
INSERT INTO users (name, id) VALUES ('Francis', 2);
INSERT INTO users (name, id) VALUES ('Peter', 3);
INSERT INTO users (name, id) VALUES ('Mike', 4);
INSERT INTO availability (answer, event_id, user_id) VALUES ('yes', 1, 1);
INSERT INTO availability (answer, event_id, user_id) VALUES ('yes', 2, 1);
INSERT INTO availability (answer, event_id, user_id) VALUES ('no', 1, 2);
INSERT INTO availability (answer, event_id, user_id) VALUES ('maybe', 2, 4);

You can cross join tables users and events to generate all possible combinations, and then left join table availability. Then, aggregation and a conditional sum() give you the expected result.
select
u.id user_id,
sum(case when a.event_id is null then 1 else 0 end) cnt
from users u
cross join events e
left join availability a
on a.event_id = e.id and a.user_id = u.id
group by u.id
order by u.id
Demo on DB Fiddle:
user_id | cnt
------: | --:
1 | 0
2 | 1
3 | 2
4 | 1

You can also solve this by pre-aggregating the availability table:
select u.user_id, (e.num_events - a.cnt)
from users u left join
(select a.user_id, count(*) as cnt
from availability a
group by a.user_id
) a
on u.user_id = a.user_id cross join
(select count(*) as num_events from events) e;

Related

How to count correctly with more than one join?

I have to count 2 things in differents tables.
Tables:
[course]
id | name
1 | Math
2 | History
3 | English
[class]
id | course_id
1 | 1
2 | 1
3 | 2
4 | 2
5 | 2
6 | 3
7 | 3
8 | 3
[lesson]
id | name | course_id
1 | Basic | 1
2 | Intermediate | 1
3 | Advanced | 3
When I user This queryto get ONE count and ONE join:
SELECT course.id, course.name, count(class.course_id) as totalCourses
FROM course
LEFT JOIN class ON class.course_id = course.id
GROUP BY course.id
I get the right result:
id name totalCourses
1 | Math | 2
2 | History | 3
3 | English | 3
But when i use the next query to get TWO counts and TWO joins the result is messed up
SELECT course.id, course.name, count(class.course_id) as totalCourses, count(lesson.course_id) as totalLessons
FROM course
LEFT JOIN class ON class.course_id = course.id
LEFT JOIN lesson ON lesson.course_id = course.id
GROUP BY course.id
id name totalCourses totalLessons
1 | Math | 4 | 4
2 | History | 3 | 0
3 | English | 3 | 3
The result is wrong with the totalCourses AND with totalLessons
What is the right way to get TWO counts with TWO joins work?
Thanks!
you can first count the lessons number in a subselect and then join them
CREATE TABLE course
(`id` int, `name` varchar(7))
;
INSERT INTO course
(`id`, `name`)
VALUES
(1, 'Math'),
(2, 'History'),
(3, 'English')
;
CREATE TABLE class
(`id` int, `course_id` int)
;
INSERT INTO class
(`id`, `course_id`)
VALUES
(1, 1),
(2, 1),
(3, 2),
(4, 2),
(5, 2),
(6, 3),
(7, 3),
(8, 3)
;
CREATE TABLE lesson
(`id` int, `name` varchar(12), `course_id` int)
;
INSERT INTO lesson
(`id`, `name`, `course_id`)
VALUES
(1, 'Basic', 1),
(2, 'Intermediate', 1),
(3, 'Advanced', 3)
;
SELECT c.id, c.name, COUNT(cl.course_id) , l.cont_1
FROM course c
LEFT JOIN class cl ON cl.course_id = c.id
LEFT JOIN (SELECT course_id, COUNT(course_id) cont_1 FROM lesson GROUP BY course_id) l ON l.course_id = c.id
GROUP BY c.id, c.name,l.cont_1
id | name | COUNT(cl.course_id) | cont_1
-: | :------ | ------------------: | -----:
1 | Math | 2 | 2
2 | History | 3 | null
3 | English | 3 | 1
db<>fiddle here
With MySQL 8, you can do SUM() OVER...., see: https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html
drop table if exists course;
drop table if exists class;
drop table if exists lesson;
create table course (id int, name varchar(20));
create table class (id int, course_id int);
create table lesson (id int, name varchar(20), course_id int);
insert into course values
(1,'Math'),
(2,'History'),
(3,'English');
insert into class values
(1,1), (2,1),
(3,2), (4,2), (5,2),
(6,3), (7,3), (8,3);
insert into lesson values
(1,'Basic',1),
(2,'Intermediate',1),
(3,'Advanced', 3);
SELECT DISTINCT
course.id,
course.name,
-- class.course_id as class,
-- lesson.id as lesson,
-- lesson.name,
count(class.course_id) over (partition by course.id) as totalCourses,
count(lesson.id) over (partition by course.id,lesson.id) as totalLessons
FROM course
LEFT JOIN class ON class.course_id = course.id
LEFT JOIN lesson ON lesson.course_id = course.id
;
results:
+ ------- + --------- + ----------------- + ----------------- +
| id | name | totalCourses | totalLessons |
+ ------- + --------- + ----------------- + ----------------- +
| 1 | Math | 4 | 2 |
| 2 | History | 3 | 0 |
| 3 | English | 3 | 3 |
+ ------- + --------- + ----------------- + ----------------- +

MySQL - Find listings posted by multiple people

I have a schema as follows;
CREATE TABLE `vehicle` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`vehiclelicence` varchar(50) DEFAULT NULL,
`userid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`companyid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `company` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
);
insert into vehicle (vehiclelicence, userid) values ('PB1234', 1);
insert into vehicle (vehiclelicence, userid) values ('AB0987', 2);
insert into vehicle (vehiclelicence, userid) values ('UI7654', 3);
insert into vehicle (vehiclelicence, userid) values ('DV8888', 4);
insert into vehicle (vehiclelicence, userid) values ('PB1234', 2);
insert into vehicle (vehiclelicence, userid) values ('UI7654', 1);
insert into user (username, companyid) values ('Bob', 1);
insert into user (username, companyid) values ('Tim', 2);
insert into user (username, companyid) values ('Jim', 3);
insert into user (username, companyid) values ('Fred', 4);
insert into company (name) values ('Company 1');
insert into company (name) values ('Company 2');
insert into company (name) values ('Company 3');
insert into company (name) values ('Company 4');
Users add vehiclelicences all the time.
How can I return all vehiclelicences listed by more than one company and which company listed it?
This is the sql I have so far, but it lists all the records.
SELECT *
FROM vehicle v1
join user u on u.id = v1.userid
join company c on c.id = u.companyid
WHERE EXISTS
(
SELECT 1
FROM vehicle v2
WHERE v1.vehiclelicence = v1.vehiclelicence and v2.userid != v1.userid
LIMIT 1, 1
)
order by v1.vehiclelicence
;
I have created a Fiddle
This is the desired output
+----------------+--------+----------+-----------+-------------+
| vehiclelicence | userid | username | companyid | companyname |
+----------------+--------+----------+-----------+-------------+
| PB1234 | 1 | Bob | 1 | Company 1 |
| PB1234 | 2 | Tim | 2 | Company 2 |
| UI7654 | 3 | Jim | 3 | Company 3 |
| UI7654 | 1 | Bob | 1 | Company 1 |
+----------------+--------+----------+-----------+-------------+
Thank you.
WHERE v1.vehiclelicence = v1.vehiclelicence - a typo here..and remove the limit and it works.
SELECT *
FROM vehicle v1
join user u on u.id = v1.userid
join company c on c.id = u.companyid
WHERE EXISTS
(
SELECT 1
FROM vehicle v2
WHERE v1.vehiclelicence = v2.vehiclelicence and v2.userid != v1.userid
#LIMIT 1, 1
)
order by v1.vehiclelicence
;
OR start the offset from 0
SELECT *
FROM vehicle v1
join user u on u.id = v1.userid
join company c on c.id = u.companyid
WHERE EXISTS
(
SELECT 1
FROM vehicle v2
WHERE v1.vehiclelicence = v2.vehiclelicence and v2.userid != v1.userid
LIMIT 0, 1
)
order by v1.vehiclelicence
;
either way
+----+----------------+--------+----+----------+-----------+----+-----------+
| id | vehiclelicence | userid | id | username | companyid | id | name |
+----+----------------+--------+----+----------+-----------+----+-----------+
| 1 | PB1234 | 1 | 1 | Bob | 1 | 1 | Company 1 |
| 5 | PB1234 | 2 | 2 | Tim | 2 | 2 | Company 2 |
| 6 | UI7654 | 1 | 1 | Bob | 1 | 1 | Company 1 |
| 3 | UI7654 | 3 | 3 | Jim | 3 | 3 | Company 3 |
+----+----------------+--------+----+----------+-----------+----+-----------+
4 rows in set (0.001 sec)

SQL select last before by date and group by

Using MySQL, I want to select all submissions (rows) which last submission is NULL and previous one is not, grouped by user_id.
If I consider the following example table, then the answer would be rows: 2, 5 and 7.
| ID | submission | date_submission | user_id |
|----|------------|-----------------|---------|
| 1 | lorem | 2019-01-01 | 1 |
| 2 | ipsum | 2019-01-02 | 1 |
| 3 | NULL | 2019-01-03 | 1 |
| 4 | amet | 2019-01-05 | 2 |
| 5 | NULL | 2019-01-06 | 2 |
| 6 | sit | 2019-01-04 | 1 |
| 7 | sed | 2019-01-08 | 3 |
| 8 | elit | 2019-01-07 | 4 |
| 9 | NULL | 2019-01-09 | 3 |
MRE:
CREATE TABLE submissions (
id int NOT NULL,
submission varchar(45) NULL,
date_submitted date NOT NULL,
user_id int DEFAULT NULL
);
insert into submissions (1, "lorem", 2019-01-01, 1);
insert into submissions (2, "ipsum", 2019-01-02, 1);
insert into submissions (3, NULL, 2019-01-03, 1);
insert into submissions (4, "amet", 2019-01-05, 2);
insert into submissions (5, NULL, 2019-01-06, 2);
insert into submissions (6, "sit", 2019-01-04, 1);
insert into submissions (7, "sed", 2019-01-08, 3);
insert into submissions (8, "elit", 2019-01-07, 4);
insert into submissions (9, NULL, 2019-01-09, 3);
First get the last date with null submission for each user and then join to the table to get the rows of the previous dates.
By using ROW_NUMBER() get the last of these previous dates if it is not null:
select t.id, t.submission, t.date_submitted, t.user_id
from (
select s.*,
row_number() over (partition by s.user_id order by s.date_submitted desc) rn
from submissions s inner join (
select user_id,
max(case when submission is null then date_submitted end) maxnulldate
from submissions
group by user_id
) g on g.user_id = s.user_id and g.maxnulldate > s.date_submitted
) t
where t.rn = 1 and t.date_submitted is not null
See the demo.
Results:
| id | submission | date_submitted | user_id |
| --- | ---------- | -------------- | ------- |
| 2 | ipsum | 2019-01-02 | 1 |
| 4 | amet | 2019-01-05 | 2 |
| 7 | sed | 2019-01-08 | 3 |
I guess you meant row number 4 and not 5 in your expected results, right?
You can use lag() for this:
select s.*
from (select s.*,
lag(submission) over (partition by user_id order by date_submitted) as prev_submission
from submissions s
) s
where prev_submission is not null and submission is null;
Here is a db<>fiddle.
EDIT:
It occurs to me that "last submission" really is the last submission for each user. In that case, the above can be tweaked:
select s.*
from (select s.*,
row_number() over (partition by user_id order by date_submitted desc) as seqnum,
lag(submission) over (partition by user_id order by date_submitted) as prev_submission
from submissions s
) s
where prev_submission is not null and submission is null and seqnum = 1;

How to join table based on two column in mysql?

I have two tables as mentioned below.
user table
id | username | password | status |
1 | Prajna | ***** | active |
2 | Akshata | ***** | active |
3 | Sanjana | ***** | inactive |
test table
id | project_name | created_by (user id) | edited_by (user id) |
1 | Test | 1 | 2 |
2 | Trial | 1 | 1 |
3 | Pro1 | 2 | 2 |
I am trying with below query.
select project_name, user.username from test join user on user.id=test.created_by where user.status='active';
I wanted the result like below
I want to retrieve the result as below
How can I retrieve?
project_name | username(created by) | username (edited by) |
Test | Prajna | Akshata |
Trial | Prajna | Prajna |
Pro1 | Akshata | Akshata |
Try this code.
create table `user`
(
`id` int,
`username` varchar(20),
`password` varchar(20),
`status` varchar(20)
)
insert into `user` (`id`,`username`,`password`,`status`) values
(1, 'Prajna', '*****', 'active'),
(2, 'Akshata', '*****', 'active'),
(3, 'Sanjana', '*****', 'inactive')
create table `test`
(
`id` int,
`project_name` varchar(20),
`created_by` int,
`edited_by` int
)
insert into `test` (`id`,`project_name`,`created_by`,`edited_by`) values
(1, 'Test', 1, 2),
(2, 'Trial', 1, 1),
(3, 'Pro1', 2, 2)
SELECT
`t`.`project_name`,
`ua`.`username` as 'username (created by)' ,
`ub`.`username` as 'username (edited by)'
FROM `test` `t`
JOIN `user` `ua` ON `t`.`created_by` = `ua`.`id`
JOIN `user` `ub` ON `t`.`edited_by` = `ub`.`id`
WHERE
`ua`.`status` = 'active'
AND `ub`.`status` = 'active'
order by `t`.`id`
project_name | username (created by) | username (edited by)
:----------- | :-------------------- | :-------------------
Test | Prajna | Akshata
Trial | Prajna | Prajna
Pro1 | Akshata | Akshata
db<>fiddle here
SELECT
test.project_name, user.username
FROM test
INNER JOIN user
ON user.id = test.created_by
WHERE user.status='active';
PS: you have an error here user.id=test=created_by
.
You need sub-query and join between those two sub-query using join
select project_name,created_by,edited_by from
(
select u.id,project_name, u.username as created_by from user u left join test t1 on
u.id= t1.created_by
where user.status='active'
) Table1
inner join
(
select u.id,project_name, u.username as edited_by from user u left join test t2 on
u.id= t2.created_by
where user.status='active'
) table2 on Table1.project_name=table2.project_name

MYSQL: Convert a list of ids to values in a subquery

I'm trying to find a way to make my queries easier to read.
Say I have a table: with IDs, names, and partners. Partners is a list of ids.
- 0, john, null
- 1, mike, "0,2"
- 2, sarah, "0,1"
Is there a way I can do a subquery to show names instead of the partner ids?
select u.id, u.name, (select i.name from users i where i.id in u.parners) from users;
Something so I can get results like:
- 0, john, null
- 1, mike, "john,sarah"
- 2, sarah, "john, mike"
I've tried something like what I've shown above, but I can't figure out anything special. Any help would be appreciated!
Normalization is the best way for this. Otherwise, see FIND_IN_SET()
SELECT a.ID,
a.Name,
GROUP_CONCAT(b.Name) Partners
FROM tableName a
LEFT JOIN tableName b
ON FIND_IN_SET(b.id, a.partners)
GROUP BY a.ID, a.Name
SQLFiddle Demo
Suggested schema:
UserList
ID (PK)
Name
other columns
Partners
UserID (FK to UserList.ID)
PartnerID (FK to UserList.ID)
It is better to follow Normalization rules as this is a One to Many relationship.
I have created two tables here
1.users
create table users(
id int not null auto_increment,
name varchar(40) not null,
primary key(id)
);
insert into users( name) values ('John'),('Mike'),('Sarah');
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | Mike |
| 3 | Sarah |
+----+-------+
2.partners
create table users(
id int not null auto_increment,
name varchar(40) not null,
primary key(id)
);
insert into partners values (2,1),(2,3),(3,1),(3,2);
+---------+------------+
| user_id | partner_id |
+---------+------------+
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 2 | 3 |
+---------+------------+
You can use the following query to select Users and their partner
select u.name Users, group_concat(u1.name) Partners from users u
left join partners p on u.id = p.user_id
left join users u1 on p.partner_id = u1.id
group by Users;
Output:
+-------+------------+
| Users | Partners |
+-------+------------+
| John | NULL |
| Mike | John,Sarah |
| Sarah | John,Mike |
+-------+------------+