How to correctly combine these two queries? - sql

Employees table :
EmpID (Primary Key)
LastName
FirstName
Orders table :
OrderID (Primary Key)
CustID
EmpID
OrderDate
RequiredDate
ShippedDate
I need a query which returns EmpID, LastName, FirstName, total number of orders by employee, and total number of orders shipped late. An order is late if its ShippedDate is 5 days after RequiredDate. I got the late shipments by :
julianday(ShippedDate) - julianday(RequiredDate) >= 5
I thought to make two separate queries, one that takes a total count of the submissions and one for total count of late orders, then UNION them. I'm not getting the result I am looking for:
SELECT Employees.EmpId, Employees.LastName, Employees.FirstName, COUNT(*) as TotalCount, NULL
FROM Orders, Employees
WHERE Orders.EmpID = Employees.EmpID
GROUP BY LastName
UNION
SELECT Employees.EmpId, Employees.LastName, Employees.FirstName, NULL, COUNT(*) as LateCount
FROM Orders, Employees
WHERE Orders.EmpID = Employees.EmpID
AND julianday(ShippedDate) - julianday(RequiredDate) >= 5
GROUP BY LastName
I end up with offset null values on right side of the table :
TotalCount
NULL
17
NULL
NULL
25
etc.
What went wrong with my UNION? Why is the data offset and the column header wrong?
Is there a better approach?

"Is there a better approach?"
JOIN instead of UNION :
SELECT
Employees.EmpID,
Employees.LastName,
Employees.FirstName,
count(*) AS TotalCount,
sum(
julianday(Orders.ShippedDate) - julianday(Orders.RequiredDate) >= 5
) AS LateCount
FROM
Employees
JOIN Orders ON Orders.EmpID = Employees.EmpID
GROUP BY
Employees.EmpID
ORDER BY
TotalCount DESC,
LateCount DESC

Related

Summing duplicate purchases

I am trying to write a query that selects from a list of employee_id and find duplicate book purchases (book_id) and associated cost savings (list_price). If a duplicate exists, it needs sum the prices of the amount of duplicate book_id's.
So if someone has a book costing $10 associated to their employee_id and the book is offered to them again, they don't have to buy it and there is a savings of $10. If that happens again, there's a savings of $20.
I tried a having>1 but I can't seem to get the query correct to accurately sum the savings.
Any help is appreciated.
To start,
select employee_id, book_id, count(*)
from book_purchases
group by employee_id, book_id
having count(*) > 1
gets you the list you need.
If we don't have to worry about the price changing, then we just add a column or two more to get:
select employee_id, book_id,
count(*) as copies_purchased,
sum(list_price) as total_spent,
count(*) - 1 as copies_unnecessarily_purchased,
(count(*) - 1) * avg(list_price) as amount_overspent
from book_purchases
group by employee_id, book_id
having count(*) > 1
Of course you can join to the employee and book tables to get names and titles to fat out the results a bit.
To get the total amount overspent by each employee, you could wrap the above query thusly:
select a.employee_id, sum(a.amount_overspent) as total_amount_overspent
from (
select employee_id, book_id,
count(*) as copies_purchased,
sum(list_price) as total_spent,
count(*) - 1 as copies_unnecessarily_purchased,
(count(*) - 1) * avg(list_price) as amount_overspent
from book_purchases
group by employee_id, book_id
having count(*) > 1
) as a
group by a.employee_id
Lastly, I went ahead and joined to an employee table that I presumed you have while I was at it:
select a.employee_id, emp.employee_name, sum(a.amount_overspent) as total_amount_overspent
from (
select employee_id, book_id,
count(*) as copies_purchased,
sum(list_price) as total_spent,
count(*) - 1 as copies_unnecessarily_purchased,
(count(*) - 1) * avg(list_price) as amount_overspent
from book_purchases
group by employee_id, book_id
having count(*) > 1
) as a
inner join employee as emp on emp.employee_id = a.employee_id
group by a.employee_id, emp.employee_name
To be clear, these aren't four separate queries; they're just intermediate stages in building the single query you see at the end.
I hope this helps.

How to retrieve data in the same order as the subquery?

Let us say Employee and Shippers tables have following data.
EmployeeID Name
1 Davolio
2 Fuller
3 Leverling
4 Peacock
5 Buchanan
6 Suyama
7 King
8 Callahan
9 Dodsworth
10 West
ShipperID ShipperName
1 Speedy Express
2 United Package
3 Federal Shipping
The below query returns ShipperID in descending order.
select ShipperID from Shippers order by ShipperID desc;
Now, I want to retrieve Names from the Employee table in the same order the ShipperID's are retrieved (3,2,1). My expected output is Leverling, Fuller, Davolio.
select Name from Employee where EmployeeID in (select ShipperID from Shippers order by ShipperID desc)
The above query is not returning the data as I expect. How to fix this?
UPDATE:
This is not about the ordering of records in ascending or descending order. This is just an example I have posted here. To make it more clear, assume that the subquery is returning ShipperID as 2,3,1. Now I want to retrieve the records from Employee table like Fuller, Leverling, Davolio
You will have to join the two tables if you want to keep the ordering. The order in the subquery is not preserved if you use IN
try this
select distinct Employee.Name from Employee
inner join Shippers on Shippers.ShipperID=Employee.EmployeeID
order by Shippers.ShipperID desc
Have you tried
select Name from Employee where EmployeeID in (select ShipperID from Shippers ) order by ShipperID desc
select Name from Employee where EmployeeID in (select ShipperID from Shippers ) order by EmployeeID desc
as employeeid=shipperid you can order by employeeid
If you want results to appear in a particular order, then you're going to have to pick something to order by. In your example, this means adding a column to define the order of the shippers and then joining that back to the employees table, so that you can then order the employee results accordingly.
Something like:
WITH employees AS (SELECT 1 employeeid, 'Davolio' NAME FROM dual UNION ALL
SELECT 2 employeeid, 'Fuller' NAME FROM dual UNION ALL
SELECT 3 employeeid, 'Leverling' NAME FROM dual UNION ALL
SELECT 4 employeeid, 'Peacock' NAME FROM dual UNION ALL
SELECT 5 employeeid, 'Buchanan' NAME FROM dual),
shippers AS (SELECT 1 shipperid, 'Speedy Express' shippername FROM dual UNION ALL
SELECT 2 shipperid, 'United Package' shippername FROM dual UNION ALL
SELECT 3 shipperid, 'Federal Shipping' shippername FROM dual)
-- end of mimicking your tables, see SQL below:
SELECT emp.employeeid,
emp.name
FROM employees emp
INNER JOIN (SELECT shipperid,
CASE WHEN shipperid = 1 THEN 3
WHEN shipperid = 2 THEN 1
WHEN shipperid = 3 THEN 2
END order_id
FROM shippers) shp ON emp.employeeid = shp.shipperid
ORDER BY shp.order_id ASC;
EMPLOYEEID NAME
---------- ---------
2 Fuller
3 Leverling
1 Davolio
Your order_id column may be something that already exists (e.g. a timestamp column) or generated (either by an explicit case statement as I demonstrated above, or by using the row_number() analytic function) but if you want your results to appear in a particular order, you need that column.

SELECT Employees with the top three salaries for the last two years

There are tables Employees
CREATE TABLE Employees
(
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
name nvarchar(100) NOT NULL,
depID int NOT NULL,
salary money NOT NULL,
FOREIGN KEY (depID) REFERENCES Departments(id)
);
and Payments
CREATE TABLE Payments
(
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
userID int NOT NULL,
createdDate date DEFAULT GETDATE(),
sum money NOT NULL,
FOREIGN KEY (userID) REFERENCES Employees(id)
);
I need to get names of Employees with the top three salaries for the last two years.
I tried to use the query below, but it doesn't work and I got an error.
SELECT TOP 3 name
FROM Employees
WHERE id in (SELECT id, SUM(sum) as SumTotal FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
ORDER BY SumTotal);
Error message:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
How to make it work?
This is one way to do it using ctes.
Demo
with pymt as
(
SELECT userid, sum(sum) as sumtotal
FROM Payments
WHERE createdDate BETWEEN '2013-09-01' AND '2015-09-01'
group by userid
)
, ename as
(
select e.name, pymt.sumtotal, row_number() over(order by pymt.sumtotal desc) as rn
from pymt join employees e
on pymt.userid = e.id
)
select name
from ename
where rn < = 3;
People are way over-complicating things. I'm pretty sure this will get you what you want:
SELECT TOP 3 employees.id, Name,
Sum([sum]) AS [TotalPayments]
FROM Employees
inner join Payments on employees.id = payments.userid
WHERE createdDate BETWEEN '2013-09-01' and '2015-09-01'
Group By employees.id, Name
Order BY TotalPayments DESC
SqlFiddle to test it
If you want just the names column, you could wrap that query with another select:
select Name from (
SELECT TOP 3 employees.id, Name,
Sum([sum]) AS [TotalPayments]
FROM Employees
inner join Payments on employees.id = payments.userid
WHERE createdDate BETWEEN '2013-09-01' and '2015-09-01'
Group By employees.id, Name
Order BY TotalPayments DESC
) q
Try this:-
SELECT userID, SUM(sum) as SumTotal INTO #temp
FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
group by userID
ORDER BY SumTotal
SELECT TOP 3 name
FROM Employees e join #temp
on e.id = #temp.userID
select top 3
emp.id as id,
emp.name as name,
sum(pay.sum) as total
from employees emp
join payments pay
on emp.id = pay.user_id
--where pay.createdDate BETWEEN '2015-09-01' AND '2013-09-01'
where pay.createdDate BETWEEN '2013-09-01' AND '2015-09-01'
group by emp.id, emp.name
This should work
You have to do what the error message tell you to do, put the TOP where you have your order by :
SELECT name
FROM Employees
WHERE id in (SELECT TOP 3 id, SUM(sum) as SumTotal FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
ORDER BY SumTotal)
[EDIT]
If we follow error message, this should be ok:
SELECT name
FROM Employees
WHERE id in ( Select x.userId From (SELECT TOP 3 userId, SUM([sum]) as SumTotal FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
Group By userId
ORDER BY SumTotal) x);
SELECT TOP 3 Name,
Sum(sum) AS sum
FROM Employees
WHERE createdDate BETWEEN '2015-09-01' AND '2013-09-01'
Group By Name
Order BY 2 DESC

SQL Select the highest sale for the current month

I am trying to Select the name, email address that has the highest individual sale for the current month but for some reason my select statement won't return the right date or the highest amount. Any guidance or help on right direction would be appreciated
Select
FirstName, LastName,Email, SaleDate, Max(Total)as HighestTotal
From
Sale, Employee
where
month(SaleDate) = 12
Group by
SaleDate, FirstName, LastName, Email
Use TOP 1 with ORDER BY HighestTotal desc to get the highest individual sale for the given month
Also use proper INNER JOIN syntax to join two tables. Considering that both the tables have emp_id as common column
SELECT TOP 1 WITH ties E.FirstName,
E.LastName,
E.Email,
S.SaleDate,
S.Total AS HighestTotal
FROM Sale S
INNER JOIN Employee E
ON E.emp_id = S.Emp_id
WHERE Month(S.SaleDate) = 12
ORDER BY HighestTotal DESC
With Ties will bring more than one employee in case of tie in highest individual sale.
1) You have to add a condition for the current year:
where month( SaleDate) = 12 AND year(SaleDate) = 2015
2) You have to add ORDER BY to get the highest value:
ORDER BY HighestTotal DESC
LIMIT 1

Group By on same column in subquery

I have a sql table called Employee in which I have EmployeeID, Status and DepartmentID columns. Now I have been assigned with the task of creating a tabular report where in I need count of total employees for a department, count of Employees which are active(Status), and DepartmentID.
Getting the Count of total employees and corresponding DepartmentID is simple with a Group By clause.
Select count(*) as 'Total Employees',DepartmentID from Employees
Group By DepartmentID
How would I get the Count of Employees with a particular Status for the same DepartmentID as the outer query?
Please let me know if you want some more clarifications about the problem.
If you have column Status with values active , no need of outer query
Select count(*) as 'Total Employees',
SUM(CASE WHEN status='Active' THEN 1 ELSE 0 END ) as TotalActiveEmployees,
DepartmentID
from Employees
Group By DepartmentID
Try this one.
Select count(*) as 'Total Employees',
COUNT(CASE WHEN status='Active' THEN 1 ELSE 0 END ) as TotalActiveEmployees,
DepartmentID
from Employees
Group By DepartmentID