Cannot get group by to work - sql-server

I have two tables Employee and Office and OfficeID is a foreign key in Employee table. I need to find the number of employees in each office along with the rest of the office details like office city and employee first name. I have written the following query:
select o.OfficeID, o.City, o.State, o.Country, o.ZipCode, count(e.EmployeeID)
from Office o
inner join Employee e on o.OfficeID = e.OfficeID
group by o.OfficeID
On executing I get the following message -
Column 'Office.City' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
How should I correctly use group by to solve this problem?
thanks

You either need to include all the non-aggregated columns in the select list in the group by:
select o.OfficeID, o.City, o.State, o.Country, o.ZipCode,
count(e.EmployeeID)
from Office o inner join
Employee e
on o.OfficeID = e.OfficeID
group by o.OfficeID, o.City, o.State, o.Country, o.ZipCode ;
Or, aggregate the Employee separately. Here is one method:
select o.*, e.cnt
from Office o inner join
(select e.OfficeId, count(*) as cnt
from Employee e
group by e.OfficeId
) e
on o.OfficeID = e.OfficeID;
This form is handy because you don't have the outer aggregation and can include whatever columns you like from Office in the select.
Another method uses cross apply:
select o.*, e.cnt
from Office o inner join
(select count(*) as cnt
from Employee e
where o.OfficeID = e.OfficeID
) e;
This is a bit shorter.

select o.OfficeID, o.City, o.State, o.Country, o.ZipCode, e.EmployeeID, e.FirstName, e.LastName , officeCnt.cnt
from Employee e
inner join (select OfficeId, count(*) as cnt from Employee group by OfficeId) as officeCnt
on e.OfficeID = officeCnt.OfficeID;
inner join Office o on e.OfficeID = o.OfficeID
You need to count in a subquery where you can apply group by

Related

Wrong count result

I have 2 tables in sql server:
Employee (Job Title, Hire Date)
Person (FirstName, LastName)
And I need to return the E.JobTitle, E.HireDate,P.FirstName ,P.LastName along with the how many employees have the same job title.
so I used this query:
SELECT E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName,
COUNT(E.JobTitle)
FROM AdventureWorks2019.HumanResources.Employee E
JOIN AdventureWorks2019.Person.Person P ON E.BusinessEntityID = P.BusinessEntityID
GROUP BY E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName;
the problem is that the query returns 1 per the count, while I'd expect to get per each row the count of the num employees with that job title.
my question is how can I get the correct count?
As mentioned by #Larnu in the comments, your issue is that you're grouping by LastName also, so you are going to get a count of 1 per LastName.
You need a windowed count with OVER, not an aggregated one with GROUP BY
No extra joins or grouping needed.
SELECT E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName,
COUNT(*) OVER (PARTITION BY E.JobTitle)
FROM AdventureWorks2019.HumanResources.Employee E
JOIN AdventureWorks2019.Person.Person P ON E.BusinessEntityID = P.BusinessEntityID;
You should count the number of employee for each job title first in a subquery or cte.
SELECT
JobTitle,
COUNT(*) AS Count
FROM HumanResources.Employee
GROUP BY JobTitle
Then you can join the subquery or the cte with your original query.
WITH cte
AS
(
SELECT
JobTitle,
COUNT(*) AS Count
FROM HumanResources.Employee
GROUP BY JobTitle
)
SELECT
E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName,
cte.Count
FROM
HumanResources.Employee E
INNER JOIN Person.Person P ON P.BusinessEntityID = E.BusinessEntityID
INNER JOIN cte on cte.JobTitle = E.JobTitle
ORDER BY
E.JobTitle,
P.LastName,
P.FirstName
;

SQL Server - Filter data using a query which is dependent on another query

I have a query which returns few emmployee id's and the query looks like below
select E.EmpID as EmployeeID
from tblEmployee E
where EmpRole in (1,2,3) and E.Test like '%PS%'
I have another query which looks like below one
Select E.EmpID and EmployeeID, B.CountryId, B.CountryName, B.StateId,
B.StateName
from tblEmployeeInfo E
inner join tblTest B
where E.StateId = B.StateId and E.CountryId = B.CountryId
My requirement is that the second query needs to return data of only those employees which are resulted in 1st query...
Both are different tables, how can i join these both ?
use a Select inside the IN clause
Select E.EmpID as EmployeeID, B.CountryId, B.CountryName, B.StateId,
B.StateName
from tblEmployeeInfo E
inner join tblTest B
where E.StateId = B.StateId and E.CountryId = B.CountryId
and E.EmpID IN (select E.EmpID
from tblEmployee E
where EmpRole in (1,2,3) and E.Test like '%PS%'
)
You could do another JOIN in tblEmployee :
Select E.EmpID and EmployeeID, B.CountryId, B.CountryName, B.StateId,
B.StateName
from tblEmployeeInfo E
JOIN tblTest B ON E.StateId = B.StateId and E.CountryId = B.CountryId
JOIN tblEmployee E2 ON E.EmpID = E2.EmpID AND E2.EmpRole in (1,2,3) AND E2.Test like '%PS%'

Select all the data from another table even if corresponding value from another table is NULL

I have this query:
SELECT city.CITY_NAME,
SUM(case when c.CUSTOMER_ID=o.CUSTOMER_ID and o.ORDER_ID=od.ORDER_ID
then od.TOTAL_AMT_PER_ITEM
else 0 end) AS TOTAL_AMT_PER_ITEM
FROM [ORDER] o
INNER JOIN ORDER_DETAILS od
ON o.ORDER_ID = od.ORDER_ID
INNER JOIN CUSTOMER c
ON o.CUSTOMER_ID = c.CUSTOMER_ID
INNER JOIN CUSTOMER_ADDRESS ca
ON ca.CUSTOMER_ID = c.CUSTOMER_ID
INNER JOIN CITY city
ON ca.CITY_ID = city.CITY_ID
GROUP BY city.CITY_NAME
I am a beginner in SQL SERVER. This query displays only the CITY_NAME that has a corresponding TOTAL_AMT_PER_ITEM value. What I need is to display all the CITY_NAMEs in the database even if their corresponding value is NULL. What is the work around for this? Can someone help me out? Thanks!
I change the order of the joins maybe that help.
You start with CITY because is the source for your GROUP BY and try to see if have any CUSTOMER_ADDRESS.
I guess if is a new store you can have 0 customers.
Then INNER JOIN because direction cant exist alone, they belong to a customer
Then LEFT JOIN because again a CUSTOMER may or may not have [ORDERS].
Then INNER JOIN because every [ORDERS] have [ORDER DETAILS]
Finally you SUM(od.TOTAL_AMT_PER_ITEM) from the last JOIN table, this can get some NULL's so you need include COALESCE
SELECT city.CITY_NAME,
COALESCE(SUM(od.TOTAL_AMT_PER_ITEM) , 0) as TOTAL_AMT_PER_ITEM
FROM [CITY]
LEFT JOIN [CUSTOMER_ADDRESS] ca
ON ca.CITY_ID = [CITY].CITY_ID
INNER JOIN CUSTOMER c
ON ca.CUSTOMER_ID = c.CUSTOMER_ID
LEFT JOIN [ORDER] o
ON o.CUSTOMER_ID = c.CUSTOMER_ID
INNER JOIN ORDER_DETAILS od
ON o.ORDER_ID = od.ORDER_ID
GROUP BY [CITY].CITY_NAME
btw you should change the name of the table [Order] to [Orders] because Order is a reserved word and can cause problems.
In general I rather use the plural name for tables because is an entity saving multiple of one type
CITIES instead of CITY
CUSTOMERS intead of CUSTOMER
ORDER_DETAILS is already plural, so try to keep consistence.
SELECT
city.CITY_NAME,
SUM(od.TOTAL_AMT_PER_ITEM) AS TOTAL_AMT_PER_ITEM
FROM
CUSTOMER c
INNER JOIN
CUSTOMER_ADDRESS ca
ON ca.CUSTOMER_ID = c.CUSTOMER_ID
INNER JOIN
CITY city
ON ca.CITY_ID = city.CITY_ID
LEFT JOIN
[ORDER] o
ON o.CUSTOMER_ID = c.CUSTOMER_ID
LEFT JOIN
ORDER_DETAILS
ON o.ORDER_ID = od.ORDER_ID
GROUP BY city.CITY_NAME

only 1 value returns with sql query

AdventureWorks2012 DB - I am trying to return top 1 or 2 emlpoyees from Finance dept and Engineer dept who have worked longest. I cant get my query to return both, only results from engineering show. Any suggestions?
SELECT TOP 2 EDH.StartDate, E.BusinessEntityID, D.Name, EDH.EndDate, DATEDIFF(hour,EDH.StartDate, GETDATE()) AS HoursWorked
FROM HumanResources.Employee E
INNER JOIN Person.Person PP ON E.BusinessEntityID = PP.BusinessEntityID
INNER JOIN HumanResources.EmployeeDepartmentHistory EDH ON E.BusinessEntityID = EDH.BusinessEntityID
INNER JOIN HumanResources.Department D ON D.DepartmentID = EDH.DepartmentID
WHERE (D.Name LIKE 'Finance' OR D.Name = 'Engineering')
AND EDH.EndDate IS NULL
GROUP BY D.Name, EDH.StartDate,E.BusinessEntityID,EDH.EndDate
ORDER BY EDH.StartDate ASC
Your problem is that the employees from Engineering happen to have started before the employees from Finance. The ORDER BY is affecting all of your records (both departments), and then the TOP 2 value is grabbing the two most recent employees, regardless of departments.
If you are trying to write a query that returns the first employee from each department, you're going to have to get a bit more complex. Here is an example that uses the ROW_NUMBER() function to order employees within each department by their start date, then filters those records to only return employees who are the first individuals in their department.
SELECT
StartDate,
BusinessEntityID,
Name,
EndDate,
HoursWorked
FROM
(
SELECT
EDH.StartDate,
E.BusinessEntityID,
D.Name,
EDH.EndDate,
DATEDIFF(hour,EDH.StartDate, GETDATE()) AS HoursWorked,
ROW_NUMBER() OVER (PARTITION BY D.Name ORDER BY EDH.StartDate) AS RowNumberWithinDepartment
FROM
HumanResources.Employee E
INNER JOIN
Person.Person PP ON E.BusinessEntityID = PP.BusinessEntityID
INNER JOIN
HumanResources.EmployeeDepartmentHistory EDH ON E.BusinessEntityID = EDH.BusinessEntityID
INNER JOIN
HumanResources.Department D ON D.DepartmentID = EDH.DepartmentID
WHERE
(D.Name LIKE 'Finance' OR D.Name = 'Engineering') AND
EDH.EndDate IS NULL
GROUP BY D.Name, EDH.StartDate,E.BusinessEntityID,EDH.EndDate
) x
WHERE RowNumberWithinDepartment = 1
ORDER BY StartDate ASC

SQL UNION INNER JOIN

Im trying to select from 2 tables that have the same columns, but both tables have an inner join -
select e.ID,
c.FullName,
car.VIN,
car.Registration,
e.Telephone,
e.Mobile,
e.Email,
e.EstimateTotal,
e.LastUpdated,
e.LastUpdateBy from (select id from Estimates UNION ALL select id from PrivateEstimates) e
inner join Customers c on c.ID = e.CustomerID
inner join Cars car on car.ID = e.CarID
where e.Status = 0
The trouble is, it can't find e.CustomerID, e.CarID or e.Status on the inner join? Any ideas?
Your subquery
select id from Estimates
union all
select id from PrivateEstimates
returns only a single id column. Include necessary columns in the subquery if you want to use those columns in JOIN statements

Resources