1 to many Select query with aggregate function (Adventure Works database) - sql

I am practicing my SQL with the Adventure Works Database.
My task is simple. I want to look up an employee and see how much money they make.
First Name| Last Name| Age| Pay Rate|
The problem is that the Pay Rate is located in a table with a 1 to many relationship with the employee (EmployeePayHistory) with the column ModifiedDate. I want to grab the most recent ModifiedDate but nothing I have tried works. I keep getting caught up on the aggregate function in my sub query
SELECT e.BusinessEntityID,p.FirstName [First Name], p.LastName [Last Name], DATEDIFF(YEAR,e.BirthDate, GETDATE() )[Age],
(SELECT eph1.Rate FROM HumanResources.EmployeePayHistory eph1 HAVING eph1.Rate = MAX(eph.ModifiedDate))
FROM Person.Person p
JOIN HumanResources.Employee e ON p.BusinessEntityID = e.BusinessEntityID
JOIN HumanResources.EmployeePayHistory eph ON e.BusinessEntityID = eph.BusinessEntityID
GROUP BY e.BusinessEntityID, p.FirstName,p.LastName, DATEDIFF(YEAR,e.BirthDate, GETDATE() )

This is a greatest-n-per-group problem. Don't think aggregation: think filtering instead.
I guess this does what you want:
SELECT
e.BusinessEntityID
p.FirstName [First Name],
p.LastName [Last Name],
DATEDIFF(YEAR,e.BirthDate, GETDATE()) [Age],
(
SELECT TOP (1) eph.Rate
FROM HumanResources.EmployeePayHistory eph
WHERE eph.BusinessEntityID = p.BusinessEntityID
ORDER BY eph.ModifiedDate DESC
) [LatestRate]
FROM Person.Person p
JOIN HumanResources.Employee e ON p.BusinessEntityID = e.BusinessEntityID
Note: you did not tell exactly what the relation is between Person and Employee - the above assumes that this is a 1-1 relationship.

Related

Which employee has processed most orders?

I was trying to do my exercise to get an answer to this question,
--Which employee has processed most orders?
following the table below;
and in SQL the query became this monster
select e.employeeid, e.lastname, count(*) as 'aantal' from employee e
join orders o on e.EmployeeID = o.EmployeeID group by e.EmployeeID,
e.LastName having count(*) = (
select top 1 count(*) as 'aantal' from employee e
join orders o on e.EmployeeID = o.EmployeeID
group by e.FirstName
order by count(*) desc
)
my question is, is there really no other ways to get an answer from this:
select e.employeeid, e.lastname, count(*) as 'aantal' from employee
join orders o on e.EmployeeID = o.EmployeeID
group by e.EmployeeID, e.LastName
and then just find a maximum of these table??
like: max(and here query)
You probably can try this:
select e.employee_id, e.title, count(o.order_id) as c from orders o join employees e on e.employee_id = o.employee_id group by e.employee_id, e.title order by c desc;
Answer to my question.
This is just impossible in SQL
https://www.youtube.com/watch?v=8hyCpyhj8Jc&ab_channel=DaveSullivan

Retrieving records with inner joins

My assignment is to get the the First name, Middle name and Last name for all Customers that have had an order before '2012-09-30' and after '2013-09-30'. I'm using the AdventureWorks2017 as a sample DB
Table: Sales.SalesOrderHeader
[SalesOrderID]
,[RevisionNumber]
,[OrderDate]
,[DueDate]
,[ShipDate]
,[Status]
,[OnlineOrderFlag]
,[SalesOrderNumber]
,[PurchaseOrderNumber]
,[AccountNumber]
,[CustomerID]
,[SalesPersonID]
,[TerritoryID]
,[BillToAddressID]
,[ShipToAddressID]
,[ShipMethodID]
,[CreditCardID]
,[CreditCardApprovalCode]
,[CurrencyRateID]
,[SubTotal]
,[TaxAmt]
,[Freight]
,[TotalDue]
,[Comment]
,[rowguid]
,[ModifiedDate]
Table: Person.Person
[BusinessEntityID]
,[PersonType]
,[NameStyle]
,[Title]
,[FirstName]
,[MiddleName]
,[LastName]
,[Suffix]
,[EmailPromotion]
,[AdditionalContactInfo]
,[Demographics]
,[rowguid]
,[ModifiedDate]
Table: Sales.Customers
[CustomerID]
,[PersonID]
,[StoreID]
,[TerritoryID]
,[AccountNumber]
,[rowguid]
,[ModifiedDate]
My Query
SELECT DISTINCT person_table.FirstName,
person_table.MiddleName,
person_table.LastName
FROM Sales.SalesOrderHeader as sales_order_table
inner join Sales.Customer as sales_customer_table
on (sales_customer_table.CustomerID = sales_order_table.CustomerID
and sales_order_table.OrderDate <= '2012-09-30' )
inner join Sales.Customer as sales_customer_table2
on (sales_customer_table2.CustomerID = sales_order_table.CustomerID
and sales_order_table.OrderDate >= '2013-06-30' )
inner join Sales.Customer as match_result
on (match_result.CustomerID = sales_customer_table2.CustomerID)
inner join Person.Person as person_table
on (person_table.BusinessEntityID = match_result.PersonID)
In this current state returns no rows and im unsure where the problem is
[UPDATE]
Found a relatevly good solution to the problem by editing Bilal Fakih answer
SELECT DISTINCT person_table.FirstName,
person_table.MiddleName,
person_table.LastName,
count(*) as Total_Instanses
FROM Sales.SalesOrderHeader as sales_order_table
inner join Sales.Customer as sales_customer_table
on (sales_customer_table.CustomerID = sales_order_table.CustomerID)
inner join Person.Person as person_table
on (person_table.BusinessEntityID = sales_customer_table.PersonID)
WHERE sales_order_table.OrderDate NOT BETWEEN '2012-09-30' AND '2013-06-30'
GROUP BY person_table.FirstName,
person_table.MiddleName,
person_table.LastName
HAVING count(*) >= 2
The suggestion was good but it woud return records that only had one instance. Im running into a few corner cases now. For example If a person has made 2 Orders that are bewfore 2012 or after 2013 will still be shown. The result im looking for is for a person to show up only when he has made orders before AND after the given dates
Try this, I'm not sure if it works I don't have the dataset to test, but it should
SELECT DISTINCT person_table.FirstName,
person_table.MiddleName,
person_table.LastName
FROM Sales.SalesOrderHeader as sales_order_table
inner join Sales.Customer as sales_customer_table
on (sales_customer_table.CustomerID = sales_order_table.CustomerID
inner join Person.Person as person_table
on (person_table.BusinessEntityID = match_result.PersonID)
WHERE sales_order_table.OrderDate NOT BETWEEN '2012-09-30' AND '2013-06-30'
You could simply this using below. Also your dates filter was not correct.
SELECT DISTINCT p.FirstName,
p.MiddleName,
p.LastName
FROM Sales.SalesOrderHeader as s
INNER JOIN Sales.Customer as c
ON c.CustomerID = s.CustomerID
INNER JOIN Person.Person as p
ON p.BusinessEntityID = c.PersonID)
WHERE s.OrderDate >= '2012-09-30' <----- add this
AND s.OrderDate <= '2013-06-30' ) ---- and this
My assignment is to get the the First name, Middle name and Last name for all Customers that have had an order before '2012-09-30' and after '2013-09-30'.
One method uses aggregation:
SELECT p.FirstName, p.MiddleName, p.LastName
FROM person_table p JOIN
Sales.Customer c
ON p.BusinessEntityID = c.PersonID JOIN
Sales.SalesOrderHeader so
ON c.CustomerID = so.Cus tomerID
GROUP BY p.FirstName, p.MiddleName, p.LastName
HAVING MIN(so.OrderDate) < '2020-09-30' AND
MAX(so.OrderDate) >'2013-06-30';
I will say that this condition looks suspicious:
ON p.BusinessEntityID = c.PersonID
However, that is what you use in your query. I would expect the person table to have an id called something like PersonId.

SQL query: Fetch Employee Name (i.e. FirstName space LastName), Department Name, Manager, City, StateProvinceCode, CountryRegionCode

CONDITIONS: Where city is 'New York'. If Manager is NULL then display 'CEO'.
I have shared the link of table structure.
I understand that I may be able to achieve this via sub-queries, CTE or a Self Join. But I am new to CTEs/Self Joins so need some help.
Trying something like below: But I am very confused how to do this.
WITH CTE EmployeeManager(
Select Concat(FirstName, ,LastName) as [Employee Name]
FROM Contact C JOIN Employee E ON C.ContactID=E.ContactID
Where E.ManagerID IS NULL
UNION ALL
Select Concat(FirstName, ,LastName) as [Employee Name]
FROM Contact C JOIN Employee E ON C.ContactID=E.ContactID
JOIN EmployeeManager EM ON E.ManagerID=EM.EmployeeID
)
Select [Employee Name] from EmployeeManager;
Not able to proceed further. Please help...
I think the below should work or point you in the right direction. I've assumed a couple of thing about you data:
1) Managers are found by looking up and ManagerID against EmployeeID in the Employee table
2) Employees in their history may have worked in more than one department and you are only looking for current employees so EndDate in EmployeeDepartmentHistory IS NULL
3) Employees may have multiple address stored so I pick the latest one by using a subquery
SELECT
Concat(C.FirstName,' ',C.LastName) as [Employee Name],
D.[Department Name],
COALESCE(Concat(M.FirstName,' ',M.LastName),'CEO') as [Manager Name],
A.City,
SP.StateProvinceCode,
SP.CountryRegionCode
FROM Employee AS E
LEFT JOIN Contact AS C ON E.ContactID=C.ContactID
LEFT JOIN EmployeeDepartmentHistory AS EDH ON EDH.EmployeeID=E.EmployeeID
LEFT JOIN Department AS D ON D.DepartmentID=EDH.DepartmentID
LEFT JOIN Employee AS Manager ON Manager.EmployeeID.=E.ManagerID
LEFT JOIN Contact AS M ON Manager.ContactID=M.ContactID
LEFT JOIN (SELECT EmployeeID, AddressID, MAX(ModifiedDate) FROM [Employee Address] GROUP BY EmployeeID, AddressID) AS EA ON E.EmployeeID=EA.EmployeeID
LEFT JOIN Address AS A ON EA.AddressID=A.AddressID
LEFT JOIN StateProvince AS SP ON SP.StateProvinceID=A.StateProvinceID
WHERE EDH.EndDate IS NULL
AND A.City='New York'

Blank Table Result

I am having a problem with my database. I have replicated it using adventure works 2014.
I want to show all results where the BusinessEntityID shows more than once. So if a user has been a member of two deparments, their ID will show twice
But this is what I get with the below query.
SELECT Person.FirstName,
Person.LastName,
HumanResources.Department.Name AS CurrentDepartment,
StartDate,
EndDate
FROM AdventureWorks2014.Person.Person
JOIN HumanResources.EmployeeDepartmentHistory
ON HumanResources.EmployeeDepartmentHistory.BusinessEntityID = Person.BusinessEntityID
JOIN HumanResources.Department
ON EmployeeDepartmentHistory.DepartmentID = HumanResources.Department.DepartmentID
GROUP BY Person.BusinessEntityID,
HumanResources.Department.DepartmentID,
Person.FirstName,
Person.LastName,
HumanResources.Department.Name,
StartDate,
EndDate
HAVING COUNT(Person.BusinessEntityID) > 1
ORDER BY Person.LastName, StartDate
I remove the Having I do get returned result(the whole table). So I think I know where the problem is not what it is / how to resolve it.
Im going assume your query works ok and if you dont include the group by will bring all the employees. So you need join with a list of employees with +1 department
JOIN (SELECT P.BusinessEntityID --, COUNT(EDH.DepartmentID) for debug
FROM AdventureWorks2014.Person.Person P
JOIN HumanResources.EmployeeDepartmentHistory EDH
ON P.BusinessEntityID = EDH.BusinessEntityID
GROUP BY P.BusinessEntityID
HAVING COUNT(EDH.DepartmentID) > 1
) as list_of_employees_with_two_or_more
ON AdventureWorks2014.Person.Person.BusinessEntityID =
list_of_employees_with_two_or_more.BusinessEntityID
WITH cte AS (
SELECT Person.FirstName AS FirstName,
Person.LastName AS LastName,
Person.BusinessEntityID AS BusinessEntityID
FROM AdventureWorks2014.Person.Person
INNER JOIN HumanResources.EmployeeDepartmentHistory
ON HumanResources.EmployeeDepartmentHistory.BusinessEntityID = Person.BusinessEntityID
INNER JOIN HumanResources.Department
ON EmployeeDepartmentHistory.DepartmentID = HumanResources.Department.DepartmentID
GROUP BY Person.FirstName,
Person.LastName,
Person.BusinessEntityID
HAVING COUNT(*) > 1
)
SELECT Person.FirstName,
Person.LastName,
HumanResources.Department.Name AS CurrentDepartment,
StartDate,
EndDate
FROM AdventureWorks2014.Person.Person
INNER JOIN HumanResources.EmployeeDepartmentHistory
ON HumanResources.EmployeeDepartmentHistory.BusinessEntityID = Person.BusinessEntityID
INNER JOIN HumanResources.Department
ON EmployeeDepartmentHistory.DepartmentID = HumanResources.Department.DepartmentID
INNER JOIN cte t
ON Person.FirstName = t.FirstName AND
Person.LastName = t.LastName AND
Person.BusinessEntityID = t.BusinessEntityID
You need to do the grouping separately, what I think you really want is people with more than one record in EmployeeDepartmentHistory, e.g.
SELECT BusinessEntityID
FROM HumanResources.EmployeeDepartmentHistory
GROUP BY BusinessEntityID
HAVING COUNT(*) > 1
I think the most efficient way to integrate this into your current query is by using EXISTS:
SELECT p.FirstName,
p.LastName,
d.Name AS CurrentDepartment,
edh.StartDate,
edh.EndDate
FROM Person.Person AS p
JOIN HumanResources.EmployeeDepartmentHistory AS edh
ON edh.BusinessEntityID = p.BusinessEntityID
JOIN HumanResources.Department AS d
ON d.DepartmentID = edh.DepartmentID
WHERE EXISTS
( SELECT 1
FROM HumanResources.EmployeeDepartmentHistory AS edh2
WHERE edh2.BusinessEntityID = p.BusinessEntityID
HAVING COUNT(*) > 1
)
ORDER BY p.LastName, StartDate

Working on Adventureworks2014, not joining correctly

Using SqlServer 2014.
Below is my good and bad code. I am trying to query the HumanResources.Employee db, and include two things with it, the most recent pay upgrade in pay from the HumanResources.EmployeePayHistory in the form of "most recent date", and the First, Middle, and Last Names from the Person.Person file.
I was able to query the employee data and include the names successfully.
select b.*,(a.FirstName +' '+ isnull(a.MiddleName,'')+' '+a.LastName) as WHOLE_NAME
from Person.Person a
join HumanResources.Employee b
on a.BusinessEntityID = b.BusinessEntityID
I was able to then figure out how to get the correct date format out of the pay history file...
Select Convert(VarChar(10),ModifiedDate,101) as Date
From HumanResources.EmployeeDepartmentHistory
I know that I can find the MAX date for each pay upgrade like this:
Select BusinessEntityID, MAX(ModifiedDate) as MostRecent
From HumanResources.EmployeePayHistory
Group By BusinessEntityID
But when I try to combine three tables, my brain pretty much melts and I keep blowing it. Here is my mess:
Select Convert(VarChar(10),c.ModifiedDate,101) as Date, b.*,(a.FirstName +' '+ isnull(a.MiddleName,'')+' '+a.LastName) as WHOLE_NAME
From Person.Person a inner join
HumanResources.Employee b on a.BusinessEntityID = b.BusinessEntityID
inner join
(Select BusinessEntityID, MAX(ModifiedDate) as MostRecent
From HumanResources.EmployeePayHistory
Group By BusinessEntityID)
HumanResources.EmployeePayHistory c
on c.businessEntityID = a.BusinessEntityID
Can you help me fix this final three-table join attempt?
Many thanks.
Change this Convert(VarChar(10),c.ModifiedDate,101) as Date to this Convert(VarChar(10),c.MostRecent,101) as Date (since you have named the column you are referencing as MostRecent)
Change your alias on your derived table join to simply c
Whole query:
SELECT Convert(VarChar(10),c.MostRecent,101) as Date,
b.*,
(a.FirstName +' '+ isnull(a.MiddleName,'')+' '+a.LastName) as WHOLE_NAME
FROM Person.Person a
INNER JOIN HumanResources.Employee b on a.BusinessEntityID = b.BusinessEntityID
INNER JOIN (SELECT BusinessEntityID, MAX(ModifiedDate) as MostRecent
FROM HumanResources.EmployeePayHistory
GROUP BY BusinessEntityID
) c on c.businessEntityID = a.BusinessEntityID
I think you are overthinking this. You can do 2 easy joins and then MAX() the last pay raise from the pay history table. I don't think you need to touch the employee dept history table.
SELECT c.*
, a.businessentityid
, FirstName + ISNULL(middlename, '') + lastname AS fullname
, CONVERT(VARCHAR(10), MAX(b.ratechangedate), 101) AS last_pay_increase
FROM person.person a
INNER JOIN HumanResources.EmployeePayHistory b
ON b.BusinessEntityID = a.BusinessEntityID
INNER JOIN HumanResources.Employee c
ON c.BusinessEntityID = a.BusinessEntityID
GROUP BY c.BusinessEntityID
, c.NationalIDNumber
, c.LoginID
, c.OrganizationNode
, c.OrganizationLevel
, c.JobTitle
, c.BirthDate
, c.MaritalStatus
, c.Gender
, c.HireDate
, c.SalariedFlag
, c.VacationHours
, c.SickLeaveHours
, c.CurrentFlag
, c.rowguid
, c.ModifiedDate
, a.businessentityid
, FirstName + ISNULL(middlename, '') + lastname