How to get the values from 2 tables together in SQL - sql

I have 2 tables
1. emp_mst
empcode empname
001 abc
002 def
2. leavetotal
empcode leave
001 10
001 5
001 2
002 12
002 8
Now i am trying to get the empcode and empname from the emp_mst
and the total for leave days from leavetotal.I have no idea how to get it.
thanks in advance.

In the FROM clause you can specify multiple tables, this will result in a cartesian product of the two tables. So each row in the a table will be joined with every other row in all other tables. This is of course not what you want, you only want rows with the same empcode to be joined. So that needs to be specified in the WHERE CLAUSE.
SELECT
MST.EMPCODE,
MST.EMPNAME,
SUM(LTO.LEAVE)
FROM
EMP_MST MST,
LEAVETOTAL LTO
WHERE
MST.EMPCODE = LTO.EMPCODE
GROUP BY
EMPCODE,
EMPNAME

JOIN / GROUP BY solution:
select e.empcode, e.empname, SUM(l.leave)
from emp_mst e
left join leavetotal l on e.empcode = l.empcode
group by e.empcode, e.empname
LEFT JOIN to list even those without any leave. (Do just JOIN if not needed.)
Correlated sub-select solution:
select e.empcode, e.empname,
(select SUM(l.leave) from leavetotal l
where e.empcode = l.empcode)
from emp_mst e

You are looking for a join, and then a group by:
SELECT em.empcode, empname, SUM(leave)
FROM emp_mst em
JOIN leavetotal l ON em.empcode = l.empcode
GROUP BY em.empcode, empname

Related

Selecting the Id's that have the same EmailAddress column value

What I need:
I am looking for a solution that can give me all the Employee Id's that have the same EmailAddress Column (the filter needs to be by EmailAddress).
I want to know what are the Id's correspondent to the duplicated Email Addresses and retrieve that information.
Table Employee:
Id | PlNumber | EmailAddress | EmployeeBeginingDate | EmployedEndDate | Name UserId(FK) | CreatedBy | CreatedOn
SELECT a.Id,a.EmailAddress
FROM Employee a
INNER JOIN (SELECT
Employee.Id as EmployeeId,
Employee.EmailAddress as EmailAddress,
FROM Employee
GROUP BY Employee.Id,Employee.EmailAddress
HAVING count(Employee.EmailAddress) > 1
) b
ON a.Id= b.EmployeeId
ORDER BY a.Id
I am always getting an error:
the multi-part identifier could not be bound.
I know why the error is happening but I couldn't solve this.
UPDATE: After a few changes the query is returning 0 rows but I know it should return at least 3 rows that I have duplicate values.
Try the below query as you have an aliased table Employee as a. So in place of Employee, you have to use a.
SELECT a.Id, a.EmailAddress
FROM Employee a
INNER JOIN (SELECT
Employee.EmailAddress as EmailAddress
FROM Employee
GROUP BY Employee.EmailAddress
HAVING count(Employee.EmailAddress) > 1
) b
ON a.EmailAddress = b.EmailAddress
ORDER BY a.Id
Live db<>fiddle demo.
Assuming the ids are different on each row, I would go for exists:
SELECT e.Id, e.EmailAddress
FROM Employee e
WHERE EXISTS (SELECT 1
FROM Employee e2
WHERE e2.EmailAddress = e.EmailAddress AND
e2.Id <> e.Id
)
ORDER BY e.EmailAddress;
Or, if you want to know the number of matches, use window functions:
SELECT e.Id, e.EmailAddress, cnt
FROM (SELECT e.*, COUNT(*) OVER (PARTITION BY e.EmailAddress) as cnt
FROM Employee e
) e
WHERE cnt >= 2;

Include additional fields when using COUNT() with JOINed tables?

I'm going to learn SQL, and having troubes with one of the excercises. "Get the number of customers each employee is responsible for" (I hope the translation is OK). I figured out following:
SELECT emp.EmployeeId, COUNT(cus.SupportRepId) AS Customers
FROM Employee AS emp JOIN Customer AS cus
ON cus.SupportRepId = emp.EmployeeId
GROUP BY emp.EmployeeId
The result is correct so far:
EmployeeId Customers
-------------------------
3 21
4 20
... ...
Now I thought it would be nice to addional employee's data in the result too (e.g. JobTitle from the table 'Employee'), but this doesn't seems to work:
SELECT emp.EmployeeId, emp.JobTitle, COUNT(cus.SupportRepId) AS Customers
FROM Employee AS emp JOIN Customer AS cus
ON cus.SupportRepId = emp.EmployeeId
GROUP BY emp.EmployeeId
I don't understand why. What should I have to in order to get the expected result:
EmployeeId JobTitle Customers
------------------------------------------------
3 Key Account Manager 21
4 Business Area Manager 20
... ...
I hope you can help. Have much thanks in before.
You can throw it in the GROUP BY:
SELECT e.EmployeeId, e.JobTitle, COUNT(c.SupportRepId) AS Customers
FROM Employee e JOIN
Customer c
ON c.SupportRepId = e.EmployeeId
GROUP BY e.EmployeeId, e.JobTitle;
Alternatively, you can use a correlated subquery or aggregate before joining:
SELECT e.*, c.Customers
FROM Employee e JOIN
(SELECT c.SupportRepId, COUNT(c.SupportRepId) AS Customers
FROM Customer c
GROUP BY c.SupportRepId
) c
ON c.SupportRepId = e.EmployeeId;

SQL to show date for Null value based on query

I have two tables, employee and employee time entry. I have run a query that is showing me all employees with the sum of time entered or 0 as null value. In the next column I have week number. If employee has not entered time during the week than it is giving me 0 but it is also giving me null value at the week number. how can I force query to show me week number, assuming no entry was made by employee.
Select
Concat(Empfname,Emplname) as EmployeeName,
department,
iif (sum(whours) is null, 0, sum(whours)) CurrentHours,
Datepart (ww,wdate) WeekNum
From
employee as e
left outer join
TimeEntry as w on e.id = w.eId
and wdate between '01/01/2017' and '01/31/2017'
group by
Concat(Empfname,Emplname), department, Datepart(ww, wdate)
Output
EmployeeName Department CurrentHours WeekNum
------------------------------------------------
John Smith Sales 8 1
Smith John Operations 0 Null
How can I tell it is also from WeekNum 1?
Thanks
The idea is to generate all rows using cross join and then use left join to bring in the rows you want something like this:
Select Concat(e.Empfname, e.Emplname) as EmployeeName, e.department,
coalesce(sum(whours), 0) as CurrentHours
datepart(week, wd.wdate) as WeekNum
from employee e cross join
(select distinct wdate from TimeEntry) wd left outer join
TimeEntry tw
on e.id = w.eId and tw.wdate = wd.wdate
where wd.wdate between '2017-01-01' and '2017-01-31'
group by Concat(e.Empfname, e.Emplname), e.department, Datepart(week, wd.wdate);
Try this:
Select Concat(Empfname,Emplname) as EmployeeName, department
iif (sum(whours) is null, 0, sum(whours)) CurrentHours
ISNULL(Datepart (ww,wdate),1) WeekNum
From employee as e left outer join TimeEntry as w on e.id=w.eId
and wdate between '01/01/2017' and '01/31/2017'
group by Concat(Empfname,Emplname), department, ISNULL(Datepart (ww,wdate),1)
which will force any NULL value to show 1 instead of NULL itself

Write a self join query?

I have the following table with values
CREATE TABLE #tmpEmployee(ID int, EmpName varchar(50), EmpBossID int)
insert into #tmpEmployee values ( 1, 'Abhijit', 2);
insert into #tmpEmployee values ( 2, 'Haris', 3);
insert into #tmpEmployee values ( 3, 'Sanal', 0);
Now I want the result become following
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
so I have written the following query.
select E1.ID,E1.EmpName, E.EmpName as BossName from #tmpEmployee E inner join #tmpEmployee E1 on E1.EmpBossID=E.ID.
But the problem is the 3rd employee (Sanal) has no boss.
So I want this exact result:
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
3 Sanal Null
What should I do?
Use Right Join
select E1.ID,E1.EmpName, E.EmpName as BossName from #tmpEmployee E right join #tmpEmployee E1 on E1.EmpBossID=E.ID
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
3 Sanal NULL
I think its ok for u
Use a LEFT JOIN and reverse the order of your tables:
select
E.ID,
E.EmpName,
B.EmpName as BossName
from tmpEmployee E
left join tmpEmployee B on E.EmpBossID = B.ID
See a live demo of this query on SQLFiddle
Putting the "employee" part of the join first means that all employees are listed.
Using a left join means that employees without a boss (eg the CEO) will still be listed, but will have a null for the BossName column.
If you truly want only employee listed if they have a boss, change the query to simply JOIN instead of LEFT JOIN (note that the default join type is INNER)
p.s. formatting your query doesn't hurt either:
try out this...
Use Left Join..
select E.ID,E.EmpName, E1.EmpName as BossName from #tmpEmployee E left outer join #tmpEmployee E1 on E1.EmpBossID=E.ID
ID EmpName BossName
1 Abhijit Haris
2 Haris Sanal
3 Sanal NULL

SQL Server Query using GROUP BY

I am having trouble writing a query that will select all Skills, joining the Employee and Competency records, but only return one skill per employee, their newest Skill. Using this sample dataset
Skills
======
id employee_id competency_id created
1 1 1 Jan 1
2 2 2 Jan 1
3 1 2 Jan 3
Employees
===========
id first_name last_name
1 Mike Jones
2 Steve Smith
Competencies
============
id title
1 Problem Solving
2 Compassion
I would like to retrieve the following data
Skill.id Skill.employee_id Skill.competency_id Skill.created Employee.id Employee.first_name Employee.last_name Competency.id Competency.title
2 2 2 Jan 1 2 Steve Smith 2 Compassion
3 1 2 Jan 3 1 Mike Jones 2 Compassion
I was able to select the employee_id and max created using
SELECT MAX(created) as created, employee_id FROM skills GROUP BY employee_id
But when I start to add more fields in the select statement or add in a join I get the 'Column 'xyz' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.' error.
Any help is appreciated and I don't have to use GROUP BY, it's just what I'm familiar with.
The error that you were getting is because SQL Server requires any item in the SELECT list to be included in the GROUP BY if there is an aggregate function being used.
The problem with that is you might have unique values in some columns which can throw off the result. So you will want to rewrite the query to use one of the following:
You can use a subquery to get this result. This gets the max(created) in a subquery and then you use that result to get the correct employee record:
select s.id SkillId,
s.employee_id,
s.competency_id,
s.created,
e.id employee,
e.first_name,
e.last_name,
c.id competency,
c.title
from Employees e
left join Skills s
on e.id = s.employee_id
inner join
(
SELECT MAX(created) as created, employee_id
FROM skills
GROUP BY employee_id
) s1
on s.employee_id = s1.employee_id
and s.created = s1.created
left join Competencies c
on s.competency_id = c.id
See SQL Fiddle with Demo
Or another way to do this is to use row_number():
select *
from
(
select s.id SkillId,
s.employee_id,
s.competency_id,
s.created,
e.id employee,
e.first_name,
e.last_name,
c.id competency,
c.title,
row_number() over(partition by s.employee_id
order by s.created desc) rn
from Employees e
left join Skills s
on e.id = s.employee_id
left join Competencies c
on s.competency_id = c.id
) src
where rn = 1
See SQL Fiddle with Demo
For every non-aggregated column you add to your SELECT statement you need to update your GROUP BY to include it.
This article may help you understand why.
;WITH
MAX_SKILL_created AS
(
SELECT
MAX(skills.created) as created,
skills.employee_id
FROM
skills
GROUP BY
skills.employee_id
),
MAX_SKILL_id AS
(
SELECT
MAX(skills.id) as id,
skills.employee_id
FROM
skills
INNER JOIN MAX_SKILL_created
ON MAX_SKILL_created.employee_id = skills.employee_id
AND MAX_SKILL_created.created = skills.created
GROUP BY
skills.employee_id
)
SELECT
* -- type all your columns here
FROM
employees
INNER JOIN MAX_SKILL_id
ON MAX_SKILL_id.employee_id = employees.employee_id
INNER JOIN skills
ON skills.id = MAX_SKILL_id.id
INNER JOIN competencies
ON competencies.id = skills.competency_id
If you are using SQL Server than you can use OUTER APPLY
SELECT *
FROM employees E
OUTER APPLY (
SELECT TOP 1 *
FROM skills
WHERE employee_id = E.id
ORDER BY created DESC
) S
INNER JOIN competencies C
ON C.id = S.competency_id