Select COUNT with sub-query or separate query - mysql

I'm using DataTables to display the data. It requires the total count of rows, So which approach is better for this case?
1- Sub-query:
SELECT u.id, u.name, (SELECT COUNT(id) FROM users WHERE active = 1) AS total_count FROM employees e JOIN users u ON u.id = e.user_id WHERE u.active = 1
This would return:
___________________________
| id | name | total_count |
|____|______|_____________|
| 1 | John | 7 |
| 2 | Mark | 7 |
| .. | .. | 7 |
|____|______|_____________|
2- Separate query:
SELECT u.id, u.name FROM employees e JOIN users u ON u.id = e.user_id WHERE u.active = 1
SELECT COUNT(id) FROM users WHERE active = 1
This 1st query would return:
____________
| id | name |
|____|______|
| 1 | John |
| 2 | Mark |
| .. | .. |
|____|______|
The 2nd one would return:
_____________
| COUNT(id) |
|___________|
| 7 |
|___________|

If the dataset is small then the 1st query wouldn't harm.The only drawback is since inline view is part of select statement it will get executed for each record being returned by the join between employees and users.
Here is a better option from performance standpoint,the inline view represents a table and the total_count can now be part of select statement :
SELECT u.id, u.name,total_count.tot_count
FROM (SELECT COUNT(id) tot_count FROM users WHERE active = 1) AS total_count,
employees e JOIN users u
ON u.id = e.user_id WHERE u.active = 1;

Related

How to do the join strategy in this case?

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 = ?

Query to select from one table and then from the other, based on results

I have been trying to select all things from one table and then for each result select from another table only one newest result using MySQL.
The first table is a standard one, with AI id and text name
users
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | Peter |
+----+-------+
then there is the second one with AI id, int user_id, text action and datetime date
actions
+----+---------+--------+------------+-----+
| id | user_id | action | date | str |
+----+---------+--------+------------+-----+
| 7 | 2 | drink | 2019-01-10 | 5 |
| 6 | 1 | sleep | 2019-02-14 | -2 |
| 5 | 2 | walk | 2019-04-24 | 4 |
| 4 | 1 | jump | 2019-03-14 | 3 |
| 3 | 2 | talk | 2019-04-30 | -8 |
| 2 | 2 | train | 2019-04-14 | -1 |
| 1 | 1 | drive | 2019-04-01 | 1 |
+----+---------+--------+------------+-----+
So now I want to select all from table_users and for each found row search table_actions and find only newest one, either based on id or date.
So it would look either like this (by id)
[0] => ['user_id'] = 1
['name'] = John
['action'] = sleep
['date'] = 2019-02-14
['str'] = -2
[1] => ['user_id'] = 2
['name'] = Peter
['action'] = drink
['date'] = 2019-01-10
['str'] = 5
or like this
[0] => ['id'] = 1
['name'] = John
['table_actions'] => ['id'] = 6
['user_id'] = 1
['action'] = sleep
['date'] = 2019-02-14
['str'] = -2
this sounds easy, but I tried few things and nothing worked like this. The closes one was with something like (dont have exact version on my hand, just remembering from on top of my head):
SELECT users.*, actions.*
FROM users
LEFT JOIN actions ON users.id = (
SELECT user_id FROM actions
WHERE users.id = actions.user_id
LIMIT 1
)
GROUP BY actions.user_id
with that I would get all results from users and then for each get one result from actions, but that result from actions would not be the newest one, apparently it groups as it likes to, I tried MAX(actions.id), but I have got no luck with that.
Does anyone know the solution ? for now I have to take all from users and for each result take another query in my php code and I feel there is an elegant and faster way to do that.
With this query:
select
user_id,
max(date) date
from actions
group by user_id
you get the latest date for each user and then you must join to it the 2 tables:
select
u.id, u.name, a.id, a.action, a.date, a.str
from users u
inner join actions a on a.user_id = u.id
inner join (
select
user_id,
max(date) date
from actions
group by user_id
) g on g.user_id = a.user_id and g.date = a.date
If you want the latest results by id and not by date:
select
u.id, u.name, a.id, a.action, a.date, a.str
from users u
inner join actions a on a.user_id = u.id
inner join (
select
user_id,
max(id) id
from actions
group by user_id
) g on g.user_id = a.user_id and g.id = a.id

Count rows from one tables of users in another table

I want to create a query for project listings that would give the number of registered applications, excluding the ones for which the user does not exist.
In this case, considering user 10 does not exist, I should have the query results as folows:
RESULTS
+----+------------+--------------+
| id | project | applications |
+----+------------+--------------+
| 1 | MyProject1 | 3 |
| 2 | MyProject2 | 0 |
| 3 | MyProject3 | 0 |
+----+------------+--------------+
TABLES
Projects
+----+------------+
| id | name |
+----+------------+
| 1 | MyProject1 |
| 2 | MyProject2 |
| 3 | MyProject3 |
+----+------------+
applications
+----+------+------------+
| id | user | project_id |
+----+------+------------+
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 3 | 5 | 1 |
| 4 | 10 | 1 |
+----+------+------------+
users
+----+---------+
| id | Name |
+----+---------+
| 1 | Smith |
| 2 | John |
| 3 | Paul |
| 4 | Chris |
| 5 | Gabriel |
+----+---------+
The below query is not excluding the non-existing users:
SELECT `projects` . * , (
SELECT COUNT( * )
FROM `applications`
WHERE `applications`.`project_id` = `projects`.`id`
AND EXISTS (
SELECT `applications`.`id`
FROM `applications` , `users`,`project`
WHERE `application`.`user` = `users`.`id` AND `applications`.`project_id` = `project`.`id`
)
) AS `applications`
FROM `projects` ORDER BY `id` DESC LIMIT 30
I think you want left join and group by:
select p.id, p.name, count(u.id)
from projects p left join
applications a
on p.id = a.project_id left join
users u
on a.user_id = u.id
group by p.id, p.name;
However, you might want to think about fixing the data. It seems like there should be foreign key relationships between applications and projects and applications and users. The ability to have an invalid user means that there is no valid foreign key relationship to users.
Your query looks overly complicated. This should do:
select
id,
name as project,
(
select count(*)
from applications a
where a.project_id = p.id
and a.user in (select id from users)
) as applications
from projects p;
Based on previous solution
select p.id, p.name, count(u.id)
from projects p left join
applications a
on p.id = a.project_id left join
users u
on a.user = u.id
where u.id is not null
group by p.id, p.name;
When you do a left join, if the search value doesn't exists, it returns null. Then filtering by excluding null users, will give you the result.
Please find a sqlfiddle to illustrate it : http://www.sqlfiddle.com/#!9/cbfec6/3
But easiest solution would be
select p.id, p.name, count(u.id)
from projects p,applications a, users u
where a.user = u.id
and p.id = a.project_id
group by p.id, p.name;

How to select lowest id from multiple id's on joined table

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'
);

Joining tables when certain one table has no values?

I have these tables:
USER TABLE
uid | name | role
| |
1 | bob | package 1
2 | jill | package 2
3 | pam | package 1
NODE TABLE
nid | uid | type
| |
1 | 1 | car
2 | 1 | car
3 | 1 | car
4 | 2 | page
5 | 1 | car
6 | 3 | car
If I do:
select u.uid, u.name, count(nid) as totalNodes from USER as u left join NODE on n.uid = u.uid where n.type = 'car' group by u.uid
I end up with:
uid | name | totalNodes
| |
1 | bob | 4
3 | pam | 1
In other words, Jill is excluded. Why? And how can I avoid this? I.e. I want Jill to also appear in the list, but with totalNodes as 0 (or even NULL).
You need to perform your aggregate before attempting to join the tables as what you are currently doing is left joining, then restricting the data (at which point Jill is excluded) then grouping. If you do the count and restriction in a subquery you can then left join these results to the user table for the output you want:
SELECT u.uid, u.name, IFNULL(c.nodecount,0) AS `count`
FROM USER u LEFT JOIN (
SELECT uid, `type` , COUNT(nid) AS nodecount
FROM node
WHERE TYPE = 'car'
GROUP BY uid, type
) AS c ON u.uid = c.uid
use RIGHT JOIN instead left,
try :
select u.uid, u.name, count(nid) as totalNodes from USER as u
right join NODE on n.uid = u.uid where n.type IS NULL or n.type = 'car' group by n.uid
see this excellent post a visual explanation of joins :
http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
mysql syntax of join :
http://dev.mysql.com/doc/refman/5.0/en/join.html