Getting a selection from multiple tables using JOIN - sql

There is a task: Display the hourly rates of employees, indicating the maximum rate for each department in the column Max In Department. Within each department, divide all bets into groups so that bets with the same values are part of the same group.
I wrote a request to get a breakdown of bids into groups in departments, and I ran into the problem that the maximum bid in the department is calculated by the column with the breakdown of the bid per group, but it is necessary by departments, I tried both through a separate request and a connection with this and through a nested query, I can't understand what I'm doing wrong
I will be grateful for any help!
SELECT Name AS DepartmentName
, Rate AS GroupOfRate
, MAX(eph.Rate) AS MaxRateInDepartment
FROM HumanResources.EmployeeDepartmentHistory edh
INNER JOIN HumanResources.Department d
ON edh.DepartmentID = d.DepartmentID
INNER JOIN HumanResources.EmployeePayHistory eph
ON edh.BusinessEntityID = eph.BusinessEntityID
GROUP BY Name
, Rate
The first photo is the result of my request with an error. The second photo, the tables on which the selection is made (1 table EmployeePayHistory, where the employee rates are located; 2 table EmployeeDepartmentHistory; 3 table just take the names of departments). The third photo, as it should turn out (made in Paint)
Photo #1
Photo #2
Photo #3

One possible way is to add a partition.
SELECT Name AS DepartmentName
, Rate AS GroupOfRate
, MAX(eph.Rate) OVER ( PARTITION BY Name ORDER BY Name ) AS MaxRateInDepartment
FROM HumanResources.EmployeeDepartmentHistory edh
INNER JOIN HumanResources.Department d
ON edh.DepartmentID = d.DepartmentID
INNER JOIN HumanResources.EmployeePayHistory eph
ON edh.BusinessEntityID = eph.BusinessEntityID
GROUP BY Name
, Rate
By grouping by rate, you're restricting your MAX to Department Name and Rate. The OVER clause frees it up to look only at the department name.
You can confirm by adding an ORDER BY clause to the query.
ORDER BY Name
, Rate
This should show that the first row for each department matches the department maximum.

Your GROUP BY Name, Rate clause will create separate groups for each distinct department name and rate, which means every rate within that group will be the same. That is why Max(Rate) is giving you the same value as Rate.
If you need to display individual rates and "max rate per department" on the same line, you can use a window function. SQL window functions can be thought of as defining their own scope separate from GROUP BY. In your case you want to drop the GROUP BY and calculate the MAX rate partitioned by department.
Search for "SQL window function" for more information, syntax, and examples.

Related

Why is SQL subquery in SELECT clause computed multiple times?

SELECT
P.Name,
(SELECT AVG(P1.Salary)
FROM Payroll AS P1
WHERE P.Job = P1.Job)
FROM
Payroll AS P
The query is to compute the average salary for each person's job. Why does the subquery actually return multiple tuples instead of one number (the average salary)?
IT is normal,
Using it in this subquery what you are saying if for every P.Job, compute the average salary.
You might try it this way:
SELECT P.Name, p1.AvgSalaries
FROM Payroll AS P
inner join
(SELECT P1.Job, AVG(P1.Salary) as AvgSalaries
FROM Payroll AS P1 group by P1.Job
) p1
on P.Job = P1.Job
If you have multiple "Job" available in Payroll for each "Name", your query will return multiple rows per "Name". If I guess correct, you need some GROUP BY to apply as below to achieve your required output.
SELECT P.Name, (SELECT AVG(P1.Salary)
FROM Payroll AS P1
WHERE P.Job = P1.Job)
FROM Payroll AS P
GROUP BY P.Name,P.Job
I think you want to do:
SELECT P.Name, P.Job, AVG(P.Salary)
FROM Payroll AS P
GROUP BY P.Name,P.Job
Because,SELECT or WHERE clause works on row by row basis.
In a query you if you see in subquery part of select you have referenced outer main query column. For each row, it may have different values producing different results at each row.
Basically query is looking at each employee's job and finding out avg salary offered for that job and giving it for each of the employee.

Count the number of employees for every country

I have this task:
Count the number of employees for every country. Show only those countries, when works more than 20 employees
employee_id is dedicated for Employees table
country belongs to different table - Countries table and we need country_name from this table
I have no idea how to solve this task. Below what I was able to create. I think we should use Inner Join.
SELECT a.employee_id
, b.country_name
, COUNT(a.employee_id) AS count
FROM employees a
INNER JOIN countries b ON a.employee_id = b.country_name
GROUP BY b.country_name
WHERE employee_id >20;
I think I need help from the beginning.
Thanks
Your join doesn't seem correct but as I don't know the table structure, I can't say what the right column is (I'm going to assume that it should be country_name. Even so, try this:
SELECT b.country_name
, COUNT(a.employee_id) AS count
FROM employees a
INNER JOIN countries b ON a.country_name = b.country_name
GROUP BY b.country_name
HAVING COUNT(employee_id) >20;
When grouping you need to use the HAVING statement to filter.

Select the row with the max value in a specific column, SQL Server

I've been working on a school project past few days and I picked to work on a DVD club database. I have six tables, but for this question, only two are relevant. The clients table and the loans table. So, what I am trying to do is count for every client how many loans he's made so far and out of all pick the client with the max number of loans, so he can be rewarded the free DVD next month. Here is the code I've written, but it doesn't pick the specific client, it shows all the clients having the max number of loans of a specific client:
SELECT tblClients.Client_ID, MAX(x.Number_Of_Loans) AS MAX_NOL
FROM
(
SELECT COUNT(tblLoans.Client_ID) AS Number_Of_Loans
FROM tblClients, tblLoans WHERE tblClients.Client_ID=tblLoans.Client_ID
GROUP BY tblLoans.Client_ID
)x, tblClients, tblLoans
WHERE tblClients.Client_ID=tblLoans.Client_ID
GROUP BY tblClients.Client_ID, tblClients.Given_Name,
tblClients.Family_Name, tblClients.Phone, tblClients.Address, tblClients.Town_ID
Use the following
SELECT TOP 1 tblClients.Client_ID,COUNT(tblLoans.Client_ID) AS MAX_NOL
FROM tblClients, tblLoans
WHERE tblClients.Client_ID=tblLoans.Client_ID
GROUP BY tblClients.Client_ID
ORDER BY COUNT(tblLoans.Client_ID) DESC
You can do this with a single aggregate GROUP, ordered by the client with the max loans:
SELECT TOP 1 tblClients.Client_ID, tblClients.Given_Name, tblClients.Family_Name,
tblClients.Phone, tblClients.Address, tblClients.Town_ID,
COUNT(x.Number_Of_Loans) AS MAX_NOL
FROM
tblClients INNER JOIN tblLoans
ON tblClients.Client_ID=tblLoans.Client_ID
GROUP BY tblClients.Client_ID, tblClients.Given_Name, tblClients.Family_Name,
tblClients.Phone, tblClients.Address, tblClients.Town_ID
ORDER BY MAX_NOL DESC;
Any selected columns from the client need to be included in the GROUP, and I would recommend using JOINs instead of WHERE joins.
Edit
What might be tidier is to split the determination of the ClientId with the most loans and the concern of fetching the rest of the client's data, like so (rather than the ungainly GROUP BY over many columns):
SELECT c.Client_ID, c.Given_Name, c.Family_Name,
c.Phone, c.Address, c.Town_ID,
x.MaxLoans
FROM
tblClients c
INNER JOIN
(SELECT TOP 1 tblClients.Client_ID, COUNT(tblLoans.Client_ID) AS MaxLoans
FROM tblClients
INNER JOIN tblLoans
ON tblClients.Client_ID=tblLoans.Client_ID
GROUP BY tblClients.Client_ID
ORDER BY MaxLoans DESC) x
ON c.Client_ID = x.Client_ID;

SQL aggregate functions and Group By

Employee
empId
empName
empStoreNum
Invoice
invNo
invAmount
empId
I have two tables above Employee and Invoice. I would like to setup a query to retrieve employee names, employee store numbers, and the total sales for each employee. I have issue a query below and it works but I was not able to retrieve employee store number.
SELECT Emp.empName, Sum(Inv.invAmount) AS totalSales
FROM Invoice AS Inv INNER JOIN Employee AS Emp ON Inv.empId = Emp.empId
GROUP BY Emp.empName
If I add Emp.empStoreNum to the SELECT I get the following error: “You tried to execute a query that does not include the specified expression ‘empStoreNum’ as part of an aggregate function.” How can modify the query to get employee store number also?
All non-aggregate columns in the select-list must be listed in the GROUP BY clause (unless you're using MySQL, which plays by a very different set of rules, or unless you are using a sufficiently recent version of PostgreSQL, which is able to deduce functional dependencies).
SELECT Emp.empName, Emp.empStoreNum, Sum(Inv.invAmount) AS totalSales
FROM Invoice AS Inv INNER JOIN Employee AS Emp ON Inv.empId = Emp.empId
GROUP BY Emp.empName, Emp.empStoreNum
Try adding empStoreNum to GROUP BY
SELECT Emp.empName, Emp.empStoreNum, Sum(Inv.invAmount) AS totalSales
FROM Invoice AS Inv INNER JOIN Employee AS Emp ON Inv.empId = Emp.empId
GROUP BY Emp.empName, Emp.empStoreNum
Add that second column to your group by

sql show current instance

here is the assignment that I had to face:
List the employees who have transferred between departments during their employment. You should show their current department, and the date when the transferred to the current department. This is a pretty tough query. My solution was to use a subquery to determine which employees have been in more than one department, and use that result in the base query.
The problem that I don't know how to display the employee who have been transfered and only once. The way to tell if the employee has been transfered is if in the EmployeeDepartmentHistory table, the employee id has been in more than one record (i.e. employeeID 1 is in both record 1 and record 2 because the person has been in two departments). How would I go about this? Here's what I have as of now:
SELECT EmployeeDepartmentHistory.EmployeeID,Person.Contact.FirstName, Person.Contact.LastName, Department.Name
From HumanResources.Department INNER JOIN
HumanResources.EmployeeDepartmentHistory ON
HumanResources.Department.DepartmentID =
HumanResources.EmployeeDepartmentHistory.DepartmentID INNER JOIN
HumanResources.Employee ON HumanResources.EmployeeDepartmentHistory.EmployeeID
= HumanResources.Employee.EmployeeID INNER JOIN
Person.Contact ON HumanResources.Employee.ContactID = Person.Contact.ContactID
WHERE EmployeeDepartmentHistory.EmployeeID=(SELECT COUNT(HumanResources.EmployeeDepartmentHistory.EmployeeID)
FROM HumanResources.EmployeeDepartmentHistory
WHERE EmployeeDepartmentHistory.EmployeeID = Employee.EmployeeID
Group by EmployeeDepartmentHistory.EmployeeID)
Not sure if I get the requirement correct. Would you want to try HAVING clause at the end?
That is:
HAVING COUNT(EmployeeDepartmentHistory.EmployeeID) = 2
(2 - assuming that the EmployeeDepartmentHistory will contain the current department as well)