two table EMPLOYEE and Department
EMPLOYEE's fields are ID,Name, Salary ,DEPT_ID(foreign key to department table)
DEPARTMENT'S fields are id,NAME,LOCATION
VALUES OF EMPLOYEE TABLE WILL Be
Values OF DEPARTMENT TABLE WILL BE
Output from these table should be
DEPARTMENT_Name should be alpabetically within their count If are there same Count DEPARTMENT_Name should appear in alpabetically and count will be desc order
EMPLOYEE TABLE Values
id name salary dept_id
1 Candice 4685 1
2 Julia 2559 2
3 Bob 4405 4
4 Scarlet 2305 1
5 Ileana 1151 4
Department TABLE Values
id name location
1 Executive Sydney
2 Production Sydney
3 Resources Cape Town
4 Technical Texas
5 Management Paris
OUTPUT DATA SHOULD BE
DEPARTMENT_Name Count_OF_EMPLOYEE_SAME_DEPARTMENT
Executive 2,
Technical 2,
PRODUCTION 1,
MANAGEMENT 0,
RESOURCES 0
For what you want to show all departments even if there are no employees is a LEFT JOIN. So, start with the department table (alias "d" in the query) and LEFT JOIN to the employee table (alias "e"). using shorter alias names that make sense with context makes readability easier.
Now, you have the common "count()" which just returns a count for however many records are encountered, even if multiple in the secondary (employee) table based on common ID. In addition to count(), I also did a sum of the employee salary just for purposes that you can get multiple aggregate values in the same query.. Use it or don't, just wanted to present as an option for you.
Now the order. You want that based on the highest count first, so the COUNT(*) DESC (descending order) is the first sorting. Secondary is the department name to keep alphabetized if within the same count.
select
d.`name` Department_Name,
d.Location,
count(*) NumberOfEmployees,
sum( coalesce( e.salary, 0 )) as DeptTotalSalary
from
Department d
left join employee e
on d.dept_id = e.id
group by
d.`name`
order by
count(*) desc,
d.`name`
Related
I have three tables called table a, table b and table c
all the three tables have one common column.
How to get the common maximum value from the three tables?
here is my table info:
table a
id salary
101 10000
102 15000
103 18000
table b
id salary
110 14000
127 21000
118 15000
table c
id salary
191 15000
192 20000
193 8000
my required output is :
salary
15000
Use UNION ALL (or UNION) to get the salaries of all 3 tables with an extra column, like an id, which marks the source table and then in the HAVING clause of an aggregation query check if that salary belongs to all tables.
Finally use ORDER BY with LIMIT 1 to get the max common salary:
SELECT salary
FROM (
SELECT 1 id, salary FROM tableA UNION ALL
SELECT 2 id, salary FROM tableB UNION ALL
SELECT 3 id, salary FROM tableC
) t
GROUP BY salary
HAVING COUNT(DISTINCT id) = 3 -- get only the common salaries
ORDER BY salary DESC LIMIT 1;
You can use inner joins between the tables to make sure you're only considering values that occur in all three tables.
Then use MAX() to get the greatest of such values.
SELECT MAX(salary) AS salary
FROM tableA JOIN tableB USING (salary) JOIN tableC USING (salary)
I am not getting the logic of how to do Grouping by the Department and Finding the Average of the Salary of the Department and then filtering all the rows of the table by the values that is greater than Average salary of that department in SQL
Department
Salary
A
100
B
200
A
200
B
50
So avg of group A is 150
and avg of grp B is 125
My query should return :-
Department
Salary
B
200
A
200
You should please have a look how grouping works in SQL. This query will find the department and its average salary:
SELECT department, AVG(salary) salary FROM yourtable
GROUP BY department;
In order to find the departments having a higher salary, you can just join the whole table and this "average result" and choose those entries only that have a higher salary:
SELECT y.department, y.salary FROM yourtable y
JOIN (SELECT department, AVG(salary) salary FROM yourtable
GROUP BY department) average
ON y.department = average.department
WHERE y.salary > average.salary
ORDER BY y.department;
The order clause let department A appear before department B. In your description, it's sorted the other way. If you want to change this, you can write ORDER BY y.department DESC;
A last note: If there are NULL values in the salary table, they will note be considered by the average function. So if you have 10 null values, one row with a salary of 100 and one with a salary of 50, the average will be 75 and "ignore" the NULL values. If you don't want this, you need to replace the NULL values by the value you want. As example, you could write COALESCE(salary,0) within your query if you want to replace all NULL values by zero when calculating your average salary.
I need to retrieve the employees presences for the day. There are two states in the presences: In & Out.
If the employee does not have a presence it should retrieve with the status of null.
I have two tables, Employees and Presences and I want to join them.
ID | name
1 John
2 Julie
3 Anthony
4 Joseph
Now the presences table has the following data:
ID | employee_id | presence_date | presence_hour | Movement
1 1 2016-08-30 08:55 In
2 2 2016-08-30 08:56 In
3 3 2016-08-30 08:57 In
4 1 2016-08-30 12:33 Out
5 2 2016-08-30 12:34 Out
As you can see in the presences data, the employee Anthony has not yet left the office and the employee Joseph has no entries in the table.
The result I'm expecting:
Employee | Movement
John Out
Julie Out
Anthony In
Joseph null
The query I'm using:
SELECT employee.name, presence.movement
FROM employees AS employee
LEFT JOIN presences AS presence ON presence.employee_id = employee.id
WHERE presence.presence_date = '2016-08-30' AND
employee.id IN (1, 2, 3, 4)
GROUP BY employee.id
ORDER BY employee.name, presence.id DESC
The problems I'm facing:
Joseph never appears in the data
presence.id DESC doesn't work
For Joseph presence.presence_date is null, so it is not matched by presence.presence_date = '2016-08-30'.
The order by presence.id makes no sense to me. You are grouping by employee, so all matching rows in presence for that employee are merged together. You want to sort all those according to presence.id and select the most recent rows movement value? This does not work the way you wrote it. One solution would be to use MAX(presence.id) in your query to get the id of the most recent row of presence for the current employee and then join the presence table again to get the data you want.
SELECT a.name, b.movement
FROM (
SELECT employee.name, MAX(presence.id) max_id
FROM employees AS employee
LEFT JOIN presences AS presence
ON presence.employee_id = employee.id WHERE presence.presence_date = '2016-08-30' AND
employee.id IN (1, 2, 3, 4)
GROUP BY employee.id
) a
LEFT JOIN presence b ON a.max_id = b.id
ORDER BY a.name
Although it might be not a good idea to assume that most recent is equivalent to biggest id, so one might select the row with the most recent date, but this is another "problem".
This is caused by applying the date filter in the where criteria. The where criteria is applied after the join, thus eliminating any records for Joseph, since he was not present that day. Move the date criteria to the join condition instead.
You got the whole group by wrong, your query is against the sql standards because you have columns in the select list that are not in the group by list and are not subject of an aggregate function, such as max(). MySQL allows such queries under certain sql mode settings only. Use max() on the movement and group by on employee name and date fields.
Sample query, assuming you can only have 1 in and one out per employee per day:
SELECT employee.name, max(presence.movement) as movement
FROM employees AS employee
LEFT JOIN presences AS presence ON presence.employee_id = employee.id and date(presence.presence_date) = '2016-08-30'
WHERE employee.id IN (1, 2, 3, 4)
GROUP BY employee.name, date(presence.presence_date)
I'm working on a project where I have some attendance data. I want to be able to print the top # attendees.
My query as is is set to order the list by # of events attended for each individual. I allow the user to set a limit (so, say top 50 attendees). The problem is that this doesn't do anything to account for ties, so I want to generate a rank in the query that I can then use to limit by.
My relevant schema is as follows:
Members Table:
Member Name | Member ID | # Events Attended
Events Table:
Event Name | Event ID | Other Stuff
This table is then used as a foreign key for an attendance table, which links members to events by using a foreign key that combines a Member and Event ID.
Attendance Table:
Attendance Log ID | Member FK | Event FK
So, my query as is is this:
SELECT `Member Name`, `Member ID` , COUNT( `Member ID` ) AS Attendances
FROM `Members` m
INNER JOIN
(SELECT *
FROM `Events` e
INNER JOIN `Attendance` r ON `Event ID` = `Event FK`
) er
ON `Member ID` = `Member FK`
GROUP BY `Member ID`
ORDER BY `Attendances` DESC
So, to summarize, how can I create a "rank" that I can use to limit results? So top 50 attendees is top 50 ranked attendees (so #entries >= 50), rather than 50 individuals (# entries always 50, cuts off ties).
Thanks all!
Edit1:
Sample output from query with no limit (show all results):
Member Name | Member ID | Attendances
Bob Saget 1 5
John Doe 2 4
Jane Doe 3 3
Stack Overflow 4 3
So, when users request "Show top 3 attendees" with my current query,
they would get the following:
Member Name | Member ID | Attendances
Bob Saget 1 5
John Doe 2 4
Jane Doe 3 3
when in reality, I'd like it to display the ties and show something like
Rank | Member Name | Member ID | Attendances
1 Bob Saget 1 5
2 John Doe 2 4
3 Jane Doe 3 3
3 Stack Overflow 4 3
You can try this:-
SELECT IF(Attendances = #_last_Attendances, #curRank:=#curRank, #curRank:=#_sequence) AS rank,
#_sequence:=#_sequence+1,#_last_age:=age, Member Name, Member ID,
COUNT( `Member ID` ) AS Attendances
FROM `Members` m
INNER JOIN (SELECT * FROM `Events` e
INNER JOIN `Attendance` r
ON `Event ID` = `Event FK`) er
ON `Member ID` = `Member FK`,
(SELECT #curRank := 1, #_sequence:=1, #_last_Attendances:=0) r
GROUP BY `Member Name`, `Member ID`, Rank
HAVING COUNT( `Member ID`) >= (SELECT MAX (`Member ID`)
FROM `Members`
WHERE `Member ID` < (SELECT MAX (`Member ID`)
FROM `Members`
WHERE `Member ID` < (SELECT MAX (`Member ID`)
FROM `Members`)))
ORDER BY COUNT(`Member ID`) DESC;
I think this approach will help you.
Doing this in two queries is going to be your best bet, otherwise the query gets really convoluted.
Here is a SQLFiddle showing your table schema, example data, and the queries we're talking about.
The first problem we need to break down is how to determine what the correct rank is. We can do this by doing the select but only returning a single value of the rank that is our new limit. Assuming we want the top 3 ranks we'll return only the third row (offset 2, limit 1).
# Pre-select the lowest rank allowed.
SELECT COUNT(a.attendanceId) INTO #lowestRank
FROM Member AS m
JOIN Event AS e
JOIN Attendance AS a USING (memberId, eventId)
GROUP BY m.memberId
ORDER BY 'Attendances' DESC
LIMIT 1 OFFSET 2;
Once we have the #lowestRank we can now run the query again but with a HAVING clause to restrict the GROUP BY results. By restricting only results which have a rank equal to or greater than the #lowestRank we've essentially added a LIMIT to that field.
# Return all rows of the lowest rank or above.
SELECT m.name, m.memberId, COUNT(a.attendanceId) AS 'Attendances'
FROM Member AS m
JOIN Event AS e
JOIN Attendance AS a USING (memberId, eventId)
GROUP BY m.memberId
HAVING COUNT(a.attendanceId) >= #lowestRank
ORDER BY 'Attendances' DESC;
We could have done this in one query by making the first one a JOIN of the second one, but I don't recommend that because it complicates the queries, has potential performance impact, and makes it harder to change them independently.
For example the first query only limits duplicates at the cutoff point, but if you wanted to consider all duplicates a single rank then we could change that query to only consider DISTINCT rows. In this particular data set the results would be the same, but if we had two members with four attendance then we'd still get three distinct ranks (5, 4, 4, 3, 3) versus the above query only gets two distinct ranks (5, 4, 4).
I wanted to find two maximum salaries from every department in a table which had department no., salary, and various other columns. I got this answer; it surely works but I am not able to understand the logic.
select *
from emp a where 2 > (select count( distinct(sal))
from emp
where sal > a.sal and a.deptno=deptno)
order by deptno;
For each row in employee, the query within the WHERE clause counts how many rows have a higher salary in the same department. The WHERE clause itself then restricts the results to only those salaries which have 1 or 0 rows (2 >) in the same department with a greater salary - i.e. the highest two salaries.
So with this data:
EmployeeId Sal DeptNo No. of rows in the same department with higher salary
1 1 1 3 (employees 2, 3 and 4)
2 2 1 2 (employees 3 and 4)
3 3 1 1 (employee 4)
4 4 1 0
5 1 2 2 (employees 6 and 7)
6 2 2 1 (employee 7)
7 3 2 0
...the query will select employees 3, 4, 6 and 7, as they're the employees with fewer than 2 employees who have a higher salary than them.
The inner select returns the number of higher salaries within the same department for a given employee. Now if there are less than two higher salaries within the same department then the given employee must be the top earning or next-to-top earning person within the department.
Relocate the subquery to the SELECT clause without the 'top 2' restriction (will obviously get more rows back):
select a.*,
(
select count( distinct(sal))
from emp
where sal > a.sal and a.deptno=deptno
) as tally
from emp a
You can then restrict the resultset using a WHERE clause introducing a further level e.g.
select b.*
from (
select a.*,
(
select count( distinct(sal))
from emp
where sal > a.sal and a.deptno=deptno
) as tally
from emp a
) b
where b.tally < 2
order
by b.deptno, b.tally;
The above is more verbose but maybe easier to follow the logic.