Display all available users on a given day - sql

I want to display all available users (user type: employee) on a given schedule date. They are not available if they are scheduled both day (PM/AM)
Here are my following tables:
User Types
TypeID TypeName
1 Admin
2 Employee
Users
UserID TypeID Name
1 1 Admin 1
2 2 Employee 1
3 2 Employee 2
4 1 Admin 2
5 2 Employee 3
6 2 Employee 4
7 2 Employee 5
Schedule
SchedID UserID SchedDate Day (PM/AM)
1 2 8/27/2013 PM
2 2 8/27/2013 AM
3 3 8/27/2013 AM
4 5 8/27/2013 PM
5 6 8/27/2013 AM
Expected Result (WHERE SchedDate='8/27/2013')
UserID Name
3 Employee 2
5 Employee 3
6 Employee 4
7 Employee 5
This is my current SQL statement:
SELECT Users.UserID, Users.Name FROM Users LEFT OUTER JOIN
Schedule ON Schedule.UserID = Users.UserID WHERE Users.TypeID = 5

Let's phrase this a little differently. A user is unavailable if the user has both AM and PM scheduled for the DAY column. Otherwise, the user is available.
Given that there are only two values in that column, the following query does the filtering you want:
SELECT u.UserID, u.Name
FROM Users u LEFT OUTER JOIN
Schedule s
ON s.UserID = u.UserID and
s.ScheduleDate = '2013-08-27'
WHERE u.TypeID = 5
GROUP BY u.UserID, u.Name
HAVING COUNT(distinct s.day) < 2;
If you know the values are never repeated, then you can change the having clause to:
HAVING COUNT(*) < 2;
This is a bit of a trick. When there is no match in the schedule table at all, the counts will return 0 (in the first case) or 1 (in the second case).

SELECT USERS.USERID,
USERS.NAME
FROM USERS
WHERE NOT EXISTS (SELECT SCHEDID
FROM SCHEDULE
WHERE SCHEDULE.USERID = USERS.USERID
AND DAY = 'AM')
AND NOT EXISTS (SELECT SCHEDID
FROM SCHEDULE
WHERE SCHEDULE.USERID = USERS.USERID
AND DAY = 'PM')

Related

Query to fetch user names according to a condition from another table (SQL)

I have two tables:
user
id
full_name
is_admin
is_active
1
Alan
0
1
2
Carl
0
1
3
Any
0
1
4
Jane
0
1
5
Marry
0
1
6
Pedri
0
1
7
admin
1
1
8
Mota
0
0
approver
id
subordinate_id
leader_id
main_leader_id
is_active
1
1
2
3
0
2
4
5
6
1
3
1
2
4
0
(subordinate_id, leader_id and main_leader_id are foreign keys that correspond to the id column of the user table)
I would like to perform a query that brings all user names that are not admin (user table is_admin=0) and that are active (user table is_active=1), and that if they have the id in the subordinate_id column in the approver table that only brings the name of that user that has the is_active of the approver table = 0.
That is, I would like to bring users that if they have any record as subordinate_id that only bring me those that are not active in the approver table.
I tried to get the data in the following way:
SELECT
full_name
FROM user AS U
LEFT JOIN approver AS A
ON U.id = A.subordinate_id
WHERE
A.id is null
AND
U.is_admin = 0
AND
U.is_active = 1
But with this query i only get the user name that not has a register in the approver table,
and in my case i want to get the user that have a register in the approver table as subordinate_id, but not if the register have the column 'is_active' equal to 1.
In my final result I could get something like this:
Alan
carl
any
marry
Pedri
In order to make this working, you should split the conditions in the WHERE clause into:
"user" conditions: is_admin = 0 AND is_active = 1
"approver" conditions: is not a subordinate OR is_active = 0
These two groups of conditions have to be set in AND.
SELECT DISTINCT user_.id, user_.full_name
FROM user_
LEFT JOIN approver
ON user_.id = approver.subordinate_id
WHERE (user_.is_admin = 0 AND user_.is_active = 1)
AND (approver.id IS NULL OR approver.is_active = 0)
Check the demo here.
Note: the DISTINCT keyword is necessary because the JOIN operation is made between two tables having cardinality 1:n.

Join three tables and retrieve the expected result

I have 3 tables. User Accounts, IncomingSentences and AnnotatedSentences. Annotators annotate the incoming sentences and tag an intent to it. Then, admin reviews those taggings and makes the corrections on the tagged intent.
DB-Fiddle Playground link: https://dbfiddle.uk/?rdbms=postgres_14&fiddle=00a770173fa0568cce2c482643de1d79
Assuming myself as the admin, I want to pull the error report per annotator.
My tables are as follows:
User Accounts table:
userId
userEmail
userRole
1
user1#gmail.com
editor
2
user2#gmail.com
editor
3
user3#gmail.com
editor
4
user4#gmail.com
admin
5
user5#gmail.com
admin
Incoming Sentences Table
sentenceId
sentence
createdAt
1
sentence1
2021-01-01
2
sentence2
2021-01-01
3
sentence3
2021-01-02
4
sentence4
2021-01-02
5
sentence5
2021-01-03
6
sentence6
2021-01-03
7
sentence7
2021-02-01
8
sentence8
2021-02-01
9
sentence9
2021-02-02
10
sentence10
2021-02-02
11
sentence11
2021-02-03
12
sentence12
2021-02-03
Annotated Sentences Table
id
annotatorId
sentenceId
annotatedIntent
1
1
1
intent1
2
4
1
intent2
3
2
2
intent4
4
3
4
intent4
5
1
5
intent2
6
3
3
intent3
7
5
3
intent2
8
1
6
intent4
9
4
6
intent1
10
1
7
intent1
11
4
7
intent3
12
3
9
intent3
13
2
10
intent3
14
5
10
intent1
Expected Output:
I want an output as a table which provides the info about total-sentences-annotated-per-each editor and the total-sentences-corrected-by-admin on top of editor annotated sentences. I don't want to view the admin-tagged-count in the same table. If it comes also, total-admin-corrected should return 0.
|userEmail |totalTagged|totalAdminCorrected|
|---------------|------------|---------------------|
|user1#gmail.com| 4 | 3 |
|user2#gmail.com| 2 | 1 |
|user3#gmail.com| 3 | 1 |
Query I wrote: I've tried my best. You can see that in the DB-Fiddle
My query is not resulting in the expected output. Requesting your help to achieve this.
My proposal...
SELECT UserEmail, SUM(EDICount), SUM(ADMCount)
FROM (SELECT UserAccounts.UserEmail, AnnotatedSentences.SentenceID, COUNT(*) AS EDICount
FROM AnnotatedSentences
LEFT JOIN UserAccounts ON UserAccounts.UserID=AnnotatedSentences.AnnotatorID
WHERE UserRole='editor'
GROUP BY UserAccounts.UserEmail, AnnotatedSentences.SentenceID) AS EDI
LEFT JOIN (SELECT AnnotatedSentences.SentenceID, COUNT(*) AS ADMCount
FROM AnnotatedSentences
LEFT JOIN UserAccounts ON UserAccounts.UserID=AnnotatedSentences.AnnotatorID
WHERE UserRole='admin'
GROUP BY AnnotatedSentences.SentenceID) AS ADM ON EDI.SentenceID=ADM.SentenceID
GROUP BY UserEmail
Because sentence_id might be reviewed by different users (role), you can try to use subquery (INNER JOIN between user_accounts & annotated_sentences) with window function + condition aggregate function, getting count by your logic.
if you don't want to see admin count information you can use where filter rows.
SELECT user_email,
count(Total_Tagged) Total_Tagged,
SUM(totalAdmin) totalAdmin
FROM (
SELECT ist.sentence_id,
user_email,
user_role,
count(CASE WHEN a.user_role = 'editor' THEN 1 END) over(partition by ist.sentence_id) + count(CASE WHEN a.user_role = 'admin' THEN 1 END) over(partition by ist.sentence_id) Total_Tagged,
count(CASE WHEN a.user_role = 'admin' THEN 1 END) over(partition by ist.sentence_id) totalAdmin
FROM user_accounts a
INNER JOIN annotated_sentences ats ON
a.user_id = ats.annotator_id
INNER JOIN incoming_sentences ist
ON ist.sentence_id = ats.sentence_id
) t1
WHERE user_role = 'editor'
GROUP BY user_email
ORDER BY user_email
sqlfiddle
Okay, i really rushed this so there might still be an error in the Code, but try something like this:
SELECT
a.user_email,
count(ist) Total_Tagged,
sum(innerTable.edits)
FROM
incoming_sentences ist
JOIN annotated_sentences ats ON
ist.sentence_id = ats.sentence_id
JOIN user_accounts a ON
a.user_id = ats.annotator_id
LEFT JOIN ( SELECT ics.sentence_id, count(anno.id) AS edits FROM annotated_sentences anno
LEFT JOIN user_accounts ua ON
ua.user_id = anno.annotator_id
LEFT JOIN incoming_sentences AS ics ON
ics.sentence_id = anno.sentence_id
WHERE user_role LIKE 'admin'
GROUP BY ics.sentence_id ) AS innerTable
ON innerTable.sentence_id = ist.sentence_id
GROUP BY a.user_email
The inner select should count how many admin-edits there are per post, the outer one then sums up that number for every post a user edited.
If it is guaranteed that one sentence can only be annotated once and only be reviewed once, then you can simply group by sentence and get the editor and admin. Then you group by editor and count.
select
editor,
count(*) as total_tagged,
count(admin) as total_admin_corrected
from
(
select
max(ua.user_email) filter (where ua.user_role = 'editor') as editor,
max(ua.user_email) filter (where ua.user_role = 'admin') as admin
from annotated_sentences ans
join user_accounts ua on ua.user_id = ans.annotator_id
group by ans.sentence_id
) with_editor_and_admin
group by editor
order by editor;
Demo: https://dbfiddle.uk/?rdbms=postgres_14&fiddle=e409ec49af25ac8329a99b02161832fb

How to apply a single query that sum column for individual values

I have 2 tables named user and statistics
user table has 3 columns: id, name and category
statistics table has 3 columns: id, idUser (relational), cal
something like this:
user
Id name category
1 name1 1
2 name2 2
3 name3 3
statistics
Id idUser cal
1 1 1
2 1 1
3 1 1
4 2 1
5 2 1
How can I apply a query that sum the cal column by each category of users and give me something like this:
category totalcal
1 3
2 2
3 0
You want to do a left join to keep all the categories. The rest is just aggregation:
select u.category, coalesce(sum(s.cal), 0) as cal
from users u left join
statistics s
on u.id = s.idUser
group by u.category;
Use LEFT JOIN to get 0 sum for the category=3:
SELECT
user.category
,SUM(statistics.cal) AS totalcal
FROM
user
LEFT JOIN statistics ON statistics.idUser = user.Id
GROUP BY
user.category
Here SUM would return NULL for category=3. To get 0 instead of NULL you can use COALESCE(SUM(statistics.cal), 0).

get sum of count of count postgres

I have two tables jobs and users.
Users has a one-to-many relationship with jobs.
I want to segment users into groups of jobs_done.
In other words, how many users did 1 job, 2 jobs, 3 jobs, etc
The below query does that. However, I would like to lump together all users that have done 3 or more jobs into one group.
Here is the query I currently have
select
jobs_done,
count(1) as number_of_users
from ( select
u.id,
count(*) as jobs_done
from jobs j
JOIN users u on j.user_id = u.id
group by u.id ) a
group by jobs_done
Current Output:
times_used number_of_users
1 255
2 100
3 30
4 10
5 9
Desired Output:
times_used number_of_users
1 255
2 100
3+ 49
You can use a case expression to group values 3+ into one large group. This should work:
select
case
when jobs_done >= 3 then '3+'
else cast(jobs_done as varchar(5))
end as jobs_done,
count(1) as number_of_users
from (
select
u.id,
count(*) as jobs_done
from jobs j
join users u on j.user_id = u.id
group by u.id
) a
group by case when jobs_done >= 3 then '3+'
else cast(jobs_done as varchar(5))
end;
you can group by basically everything. this is a simplistic example:
test=# SELECT CASE WHEN x < 4 THEN x::text ELSE '4+' END AS y,
count(*)
FROM generate_series(1, 10) AS x
GROUP BY y
ORDER BY 1;
y | count
----+-------
1 | 1
2 | 1
3 | 1
4+ | 7
(4 rows)

Find missing dates from multi table query

I'm looking to write a query that can count missing entries from a table of dates based on skills that a resource has to forecast availability of resource for booking. I'm not sure if it can be done and I'm certainly struggling with the logic!!
Tables
Dates
ID dateFrom StaffID
1 01-06-2014 1
2 02-06-2014 1
3 03-06-2014 1
4 04-06-2014 1
5 05-06-2014 1
6 01-06-2014 2
7 03-06-2014 2
8 04-06-2014 2
9 05-06-2014 2
10 06-06-2014 2
(Free dates on the 6th for staffID 1 and 2nd for staffID 2)
Staff
StaffID Name
1 John
2 Paul
Skills
ID StaffID SkillID
1 1 1
2 1 2
3 1 3
4 2 2
5 2 3
6 2 4
So I want to write a query that says in June, for each of the skills there is X no of days available to book. Is this even possible? looking for records that don't exist to join with a staff table?
I've put together a calendar table that can identify days without bookings but I'm struggling from there on to be honest.
Any help would be greatly appreciated!!
Steve
EDIT: DB is SQL 2005.
Expected output (if possible)
SkillID Number of days available
1 20
2 22
3 14
etc
create a calendar table with all possible dates (booked or not)
select count(distinct ad.calendarDate), s.SkillID
from all_dates ad
cross join skills s
where not exists (
select 1 from
dates where dateFrom = ad.calendarDate
and StaffID = s.StaffID
)
group by s.SkillID
If I understand your problem, your query will be some thing like:
Select sum(temp.nbrDate), temp.SkillID from
(Select s.SkillID, count (d.ID) as nbrDate from Skills s, Dates d
where s.StaffID = d.StaffID
Group by SkillID) temp
group by SkillID
If you want to add a date range, add this in your where close:
and d.DateForm between '01-06-2014' and '30-06-2014'