Working on Adventureworks2014, not joining correctly - sql

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

Related

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.

Stored procedure that selects from multiple group the results then order each group in descending order?

Here is the list of tables:
My Tables
I will like to create a stored procedure that will use the athleteId as parameter for the query and select all the columns in the result table and a few columns in each of the other tables Group the results by the EventName of the Event Table then order the groups by the Mark in descending order from the Result table and select the last record for each event.
I have search the net and tried various queries but cant seem to get the results I need.
This is hat I created. It retrieves all the info I need but I just don't seem to be able to group, order and select the lowest mark.
SELECT
a.Id as AthleteId, a.FirstName AS FirstName, a.LastName AS LastName,
a.BirthDate AS BirthDate, a.IsMale AS Male,
a.Phone AS Phone, a.Email AS Email,
p.FirstName AS ParentName, p.LastName AS ParentSurname,
p.Phone AS ParentPhone, p.Email AS ParentEmail,
ad.Street1 AS Street1, ad.Street2 AS Street2, ad.Town AS Town,
ad.Parish AS Parish, ad.Country AS Country,
s.SchoolName AS School, s.Phone AS SchoolPhone,
s.[Location] AS SchoolLocation,
c.FirstName AS CoachName, c.LastName AS CoachSurname,
c.Phone AS CoachPhone, c.Email AS CoachEmail,
m.MeetName AS MeetName,
m.StartDate AS StartDate, m.EndDate AS EndDate, m.[Location] AS MeetLocation,
e.EventName AS EventName,
r.Mark AS EventMark, r.Wind AS Wind, r.PerfDate AS PerfDate
FROM
dbo.Result r
INNER JOIN
dbo.[Event] e ON e.Id = r.EventId
INNER JOIN
dbo.Meet m ON m.Id = r.MeetId
INNER JOIN
dbo.Athlete a
JOIN
dbo.Parent p ON p.Id = a.ParentId
JOIN
dbo.[Address] ad ON ad.Id = a.AddressId
JOIN
dbo.Coach c ON c.Id = a.CoachId
JOIN
dbo.School s ON s.Id = a.SchoolId
ON a.Id = r.AthleteId
WHERE
r.AthleteId = #AthleteId
When I try adding the group by it shows an error.
Can anyone help me with this?
A quick-and-dirty way to do this in SQL Server is:
select top (1) with ties . . .
. . .
. . .
order by row_number() over (partition by r.eventid order by r.mark desc)

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

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.

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

How to SELECT unique rows from 3 JOINED tables

I am trying to check if a persons name appears multiple times in a table aswell as pull additional information (position title, department name, employee number) to limit creating two stored procedures 1. to check if name appears multiple times and 2. to get the additional information after check has been completed.
my current query right now is as follows, which returns multiple rows as the ID I am using to join the tables appears multiple times in one table (an employee can belong to multiple departments, the EmpID occurs here multiple times thyus returning the multiple values).
SELECT c.FirstName+ ' ' + c.LastName as emp_full_name, e.EmployeeNumber,
e.EmpID, dh.PositionTitle, d.Name as deptName, e.isActive
FROM Person.Contact c
INNER JOIN HumanResources.Employee e ON c.ContactID = e.ContactID
INNER JOIN HumanResources.EmployeeDepartmentHistory dh ON e.EmpID = dh.EmpID
INNER JOIN HumanResources.Department d ON dh.DepartmentID = d.DepartmentID
WHERE c.FirstName+ ' ' + c.LastName LIKE #empName
My general table structure is as follows:
Person.Contact
ContactID
FirstName
LastName
HumanResources.Employee
EmpID
EmployeeNumber
isActive
HumanResources.EmployeeDepartmentHistory
DepartmentHistoryId
EmpID
DepartmentID
PositionTitle
HumanResources.Department
DepartmentID
Name
Example Result Set:
John Doe 1234567 1 Database Architect Administration A
John Doe 1234567 1 Database Tester Administration A
Use Distinct keyword
SELECT distinct c.FirstName+ ' ' + c.LastName as emp_full_name, e.EmployeeNumber,
e.EmpID, STUFF((
SELECT ',' + edh.PositionTitle
FROM dbo.EmployeeDepartmentHistory edh
WHERE edh.EmpID = e.EmpID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''), e.isActive
FROM Person.Contact c
INNER JOIN HumanResources.Employee e ON c.ContactID = e.ContactID
INNER JOIN HumanResources.EmployeeDepartmentHistory dh ON e.EmpID = dh.EmpID
INNER JOIN HumanResources.Department d ON dh.DepartmentID = d.DepartmentID
WHERE c.FirstName+ ' ' + c.LastName LIKE #empName
Group By e.EmpID