SQL - GROUP BY and COUNT - sql

I have a table with Column - D and E.
I want to get D, Distinct E in each D, Count of total number of entry for each D. How to write SQL for this ?
Data:
D | E
-----
1 | K
1 | K
1 | A
2 | S
2 | S
2 | S
2 | S
Desired o/p:
D | E | Total_E_in_D
----------------------
1 | K | 3
1 | A | 3
2 | S | 4
SELECT D,E,Count(E in each D)
FROM table
GROUP BY D,E.
Last column should give me the total number of entries for each D.

The specific answer to the question is:
select dept, count(*) as numemployees, count(distinct emp) as numDistinctEmployees
from d1
group by dept;
This just seems quite unusual, because the it assumes that employees would be in the same department more than once.
EDIT:
Strange data format, but just use aggregation with analytic functions:
select dept, emp, sum(count(*)) over (partition by dept) as numEmployees
from d1
group by dept, emp;

You can group on the department and the employee, and join in a query where you group on the department to count the employees:
select
e.Dept,
e.Emp
d.EmpCount
from
table e
inner join (
select
Dept,
count(distinct Emp) as EmpCount
from
table
group by
Dept
) d on d.Dept = e.Dept
group by
e.Dept, e.Emp
You could also use a subquery to count the employees:
select
e.Dept,
e.Emp,
(
select
count(distinct Emp)
from
table d
where
d.Dept = e.Dept
) as EmpCount
from
table e
group by
e.Dept, e.Emp

how's this?
select dept, emp, (select count(*) from table t2 where t2.dept = t1.dept) noEmps
from table t1

Related

find a records which exists for 2 values only

I have Table structure/Data set is like this.
Emp_id Expense_amt_dollar Dept
1111 100 Marketing
1111 75 Finance
1111 25 IT
2222 100 Marketing
3333 50 Finance
4444 30 Marketing
4444 70 Finance
5555 200 IT
O/P
I am looking for Emp expense in 2 dept only
Emp_id Expense_amt_dollar Dept
1111 100 Marketing
1111 75 Finance
4444 30 Marketing
4444 70 Finance
Emp which having records for these 2 dept only.
Records should have for both dept.
With EXISTS:
select t.* from tablename t
where t.dept in ('Finance', 'Marketing')
and exists (
select 1 from tablename
where emp_id = t.emp_id and dept in ('Finance', 'Marketing') and dept <> t.dept
)
or with a CTE:
with cte as (
select t.* from tablename t
where t.dept in ('Finance', 'Marketing')
)
select c.* from cte c
where exists (
select 1 from cte
where emp_id = c.emp_id and dept <> c.dept
)
See the demo.
Results:
> EMP_ID | EXPENSE_AMT_DOLLAR | DEPT
> -----: | -----------------: | :--------
> 1111 | 75 | Finance
> 1111 | 100 | Marketing
> 4444 | 70 | Finance
> 4444 | 30 | Marketing
Emp which having records for these 2 dept only. Records should have for both dept.
I would use window functions:
select emp_id, expense_amt_dollar, dept
from (
select
e.*,
sum(case when dept in ('Marketing', 'Finance') then 1 end) over(partition by emp_id) cnt_deps
sum(case when dept not in ('Marketing', 'Finance') then 1 end) over(partition by emp_id) cnt_other_deps
from emp e
) e
where cnt_deps = 2 an cnt_other_deps is null
This gives you records for employees that belong to both departments and to no other department - which is how I understand your question. For this, you need to look over the whole table: filtering with a where clause would prevent you from checking that the employee does not belong to any other department.
Another option would be producing a subquery with having count(distinct dept) = 2 clause and then joining with the same table.
select t1.*
from tab t1
join (select emp_id, max(dept) dept1, min(dept) dept2
from tab
where dept in ('Finance', 'Marketing')
group by emp_id
having count(distinct dept) = 2) t2
on t1.emp_id = t2.emp_id
and t1.dept in (t2.dept1, t2.dept2)
You can select the marketing and finance rows and then check whether you get both for an employee:
select *
from
(
select emp_id, dept, count(*) over (partition by emp_id) as cnt
from mytable t
where dept in ('Marketing', 'Finance')
)
where cnt = 2
order by emp_id, dept;
Worked in Postgresql:
select *
from employee_expenses
where emp_id in
(
select emp_id from employee_expenses
where dept = 'Finance'
intersect
select emp_id from employee_expenses
where dept ='Marketing'
)
and dept in ('Finance', 'Marketing');

Join Tables and Return data in single row different columns with count of join

We have 3 tables in Oracle 11g, need to left join them and return the data in single row different columns with count of the join, Is there any way We can acheive the same.
Example:
Table1: (Employee_Data)
Table2: (Employee_Address)
Table3: (Employee_Role)
Expected Result:
Mack has 2 addresses and 2 roles so Emp_Addr_Count is 2, Emp_Role_Count is 2 and the related data is in same row different column.
Kindly note that EMP_ID is unique in Employee_Data table and Employee_Address and Employee_Role could be multiple or zero for a Employee.
Thanks in Advance.
Try this:
SELECT E.Emp_Id
,E.Emp_Name
,E.Emp_Age
,NVL(MAX(EA.RN),0)Addr_Count
,NVL(MAX(CASE WHEN EA.RN = 1 THEN EA.Emp_Address END),' ')Emp_Address_1
,NVL(MAX(CASE WHEN EA.RN = 1 THEN EA.Emp_City END),' ')Emp_City_1
,NVL(MAX(CASE WHEN EA.RN = 2 THEN EA.Emp_Address END),' ')Emp_Address_2
,NVL(MAX(CASE WHEN EA.RN = 2 THEN EA.Emp_City END),' ')Emp_City_2
,NVL(MAX(ER.RN1),0)Role_Count
,NVL(MAX(CASE WHEN ER.RN1 = 1 THEN ER.Emp_task END),' ')Emp_task_1
,NVL(MAX(CASE WHEN ER.RN1 = 2 THEN ER.Emp_task END),' ')Emp_task_2 FROM Employee_Data E JOIN(
SELECT Emp_Id
,ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY Emp_City desc) RN
,Emp_City
,Emp_Address
FROM Employee_Address
)EA ON EA.Emp_Id = E.Emp_Id left JOIN(
SELECT Emp_Id
,ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY Emp_Task) RN1
,Emp_task
FROM Employee_Role
)ER ON ER.Emp_Id = E.Emp_Id GROUP BY E.Emp_Id,E.Emp_Name,E.Emp_Age
Output:
EMP_ID EMP_NAME EMP_AGE ADDR_COUNT EMP_ADDRESS_1 EMP_CITY_1 EMP_ADDRESS_2 EMP_CITY_2 ROLE_COUNT EMP_TASK_1 EMP_TASK_2
1 MACK 45 2 HOME PARADISE MUM TINDER ONCLAVE DEL 2 Manage Task Resource Manage
2 JACK 30 1 BLUE PLAZA MUM 1 Code
3 ANGEL 27 1 HOME PARADISE MUM 0
You can join them as in the following statement :
WITH t AS
(
SELECT d.*, a.emp_address, a.emp_city, r.emp_task
FROM employee_data d
JOIN employee_address a on ( d.emp_id = a.emp_id )
FULL OUTER JOIN employee_role r on ( d.emp_id = r.emp_id )
)
SELECT emp_id, emp_name, emp_age, count(distinct emp_address) emp_addr_count,
min(emp_address) emp_address_1, max(emp_city) emp_city_1,
decode(min(emp_address),max(emp_address),null,max(emp_address)) emp_address_2,
decode(min(emp_city),max(emp_city),null,min(emp_city)) emp_city_2,
count(distinct emp_task) emp_role_count, min(emp_task) emp_task_1,
decode(min(emp_task),max(emp_task),null,max(emp_task)) emp_task_2
FROM t
GROUP BY emp_id, emp_name, emp_age
ORDER BY emp_id;
SQL Fiddle Demo

Finding the highest average salary of a department using joins in SQL?

I have two tables with the below schema:
Table 1
-------
empID
empName
Table 2
-------
empID
department
salary
Assuming the tables are:
Table1:
empID|empName
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
Table 2:
empID|department|salary
1 X 10
2 X 10
3 X 10
4 Y 5
5 Y 5
6 Y 5
7 Y 5
8 Y 5
9 Z 3
10 Z 3
I need to find the department name with the highest average salary and display them along with the employee names.
The output I am expecting is:
empName|department|salary
A 10
B X 10
C 10
This was an interview question, and I am recreating this from memory so it might not be perfect. I am also picking up SQL after a gap of more than 2 years. Please suggest if I am missing something.
The query that I have formed is:
SELECT
table1.empName,
TOP(1) AVG(table2.salary),
table2.department
FROM
table1
INNER JOIN
table2
ON table1.empID = table2.empID
GROUP BY
table2.department
Your syntax looks like SQL Server (the "TOP 1"). In that database, I would do something like this:
SELECT TOP (1) WITH TIES t1.empName, t2.salary, t2.department
FROM table1 t1 INNER JOIN
table2 t2
ON t1.empID = t2.empID
ORDER BY AVG(t2.salary) OVER (PARTITION BY t2.department) DESC;
A more generic solution:
SELECT empName, salary, department
FROM (SELECT t.*,
DENSE_RANK() OVER (ORDER BY avg_salary) as seqnum
FROM (SELECT t1.empName, t2.salary, t2.department,
AVG(t2.salary) OVER (PARTITION BY t2.department) as avg_salary
FROM table1 t1 INNER JOIN
table2 t2
ON t1.empID = t2.empID
) t
) t
WHERE seqnum = 1;
Based on what I draw from your question, I would approach it this way.
WITH department_rank AS
(
SELECT
department,
RANK() OVER(ORDER BY avg_salary DESC) AS avg_salary_rank
FROM
(
SELECT
department,
AVG(salary) AS avg_salary
FROM
table2
GROUP BY
department
) tbl
)
SELECT
dept.department,
emp.empID,
emp.empName,
dept.salary
FROM
table2 dept
JOIN
table1 emp
ON (emp.empID = dept.empID)
JOIN
department_rank drnk
ON (drnk.department = dept.department)
AND (drnk.avg_salary_rank = 1) --Top ranked department based on average salary
OUTPUT:

Get the group which has maximum number of records in SQL Server

[ID] [Name] [Dept]
--------------------
1 Manu A
2 Anu A
3 Tanu A
4 Danu A
5 Anu B
6 Danu B
7 Danu C
8 Anu C
9 Tanu C
10 John C
11 Anu D
12 Jane D
13 Danu D
I need to get the Dept with maximum number of employees.
Here is what I tried
SELECT
ID, Name, Dept
FROM
(SELECT
*,
rn = ROW_NUMBER() OVER(PARTITION BY Dept)
FROM Emp) t
WHERE
rn = (SELECT MAX(rn) FROM t)
I need help in the WHERE clause.
You need aggregation to count the number of employees. The approach using row_number() is one approach, but with the right query:
SELECT Dept
FROM (SELECT Dept, COUNT(*) as cnt,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM Emp
) e
WHERE seqnum = 1;
However, a more common approach would just use ORDER BY and TOP:
SELECT TOP (1) Dept, COUNT(*) as cnt
FROM emp
GROUP BY Dept
ORDER BY COUNT(*) DESC;
If you wanted ties, then you would use WITH TIES in the SELECT.
Selecting all departments having the same max number of employees:
;WITH c -- create a list of depts and number of emp's
AS (SELECT deptid,
Count(*) cnt
FROM emp
GROUP BY deptid)
SELECT d.*
FROM dept d
INNER JOIN c
ON d.deptid = c.deptid
WHERE c.cnt = (SELECT Max(cnt)
FROM c)
No WHERE is needed. Try using a GROUP BY
SELECT COUNT(Name) as NameCount, Dept from Table
GROUP BY Dept
ORDER BY COUNT(Name) DESC
The biggest group(s) will be at the top
Results
NameCount | Dept
4 A
4 C
3 D
2 B
Ok, now you added the table structure:
;WITH c
AS (SELECT Dept,
Count(*) cnt
FROM Emp
GROUP BY Dept)
SELECT c.*
FROM c
WHERE c.cnt = (SELECT Max(cnt)
FROM c)
I can't quite figure your table structure, but this selects the department with the most employees
SELECT * from Dept WHERE deptid = (
SELECT TOP 1 deptid FROM employees
GROUP BY deptid
ORDER BY COUNT(*) DESC
)

How do I get a single row value in a group statement?

I have two tables, JobTable and EmployeeTable with the following data:
EmployeeTable:
EmpId Salary
1 10
2 20
3 30
4 40
5 50
6 60
JobTable:
JobId EmpId
A 1
A 2
B 3
B 4
C 5
C 6
I need an SQL statement that will return the EmpId of the Employee with the minimum salary for each Job.
You could use the RANK() function like this:
WITH ranked AS (
SELECT
j.JobId,
e.EmpId,
e.Salary,
RANK() OVER (PARTITION BY j.JobId ORDER BY e.Salary) AS rnk
FROM JobTable j
INNER JOIN EmployeeTable e ON j.EmpId = e.EmpId
)
SELECT
JobId,
EmpId,
Salary,
FROM ranked
WHERE rnk = 1
Hmm, give this one a shot:
SELECT st.EmpID, min(st.salary)
FROM SalaryTable st INNER JOIN JobTable jt ON st.EmpID=jt.EmpID
WHERE jt.JobID = 'A'
GROUP BY st.EmpID