Select SUM, Join two tables, group sum by OrderID in SQL Query - sql

Currently trying to solve the reason for this error:
Column 'Orders.OrderID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My goal is to group all the quantities for the items in each order ID, from the Order Details table
The SQL Server database I'm using is open to the public, as part of W3School's website tools. View it here if you'd like to test your query results: https://www.w3schools.com/sql/trysqlserver.asp?filename=trysql_func_sqlserver_datepart
I appreciate any help, and please let me know if you need more context/information to help me out. Thanks!
PS: please pardon my messy code, new to SQL :)
What I have so far:
SELECT
O.OrderID,
YEAR(O.OrderDate) AS OrderYear,
DATEPART(qq, O.OrderDate) AS OrderQuarter,
CONCAT(E.FirstName, SPACE(1), E.LastName) AS EmployeeName,
C.CustomerName,
S.ShipperName,
SUM(D.Quantity) AS OrderItems
FROM
((((Orders AS O
JOIN
Employees AS E ON O.EmployeeID = E.EmployeeID)
JOIN
Customers AS C ON O.CustomerID = C.CustomerID)
JOIN
Shippers AS S ON O.ShipperID = S.ShipperID)
JOIN
OrderDetails AS D ON O.OrderID = D.OrderID);
What I'm trying to achieve: my mentor gave me a small sample image to reference from. This is how I want to print out the quantities in my query with the column name OrderItems
If you remove the SUM function from SELECT, and remove the last join table statement, you'll see my query aggregates everything correctly up until OrderItems just fine. I have yet to add the OrderRevenue column and order it by descending Revenue value, but big brownie points if you want to go the extra mile and explain how to solve that as well.

Using the example data you provided through w3c:
SELECT o.OrderID, DATEPART(YEAR,o.OrderDate) AS OrderYear, DATEPART(QUARTER,o.OrderDate) AS OrderQuarter,
E.FirstName + ' ' + E.LastName AS EmployeeName, C.CustomerName, S.ShipperName, SUM(d.Quantity) AS Quantity--, SUM(d.Quantity*d.Price) AS OrderRevenue
FROM Orders o
INNER JOIN Employees e
ON o.EmployeeID = e.EmployeeID
INNER JOIN Customers c
ON o.CustomerID = c.CustomerID
INNER JOIN Shippers s
ON o.ShipperID = s.ShipperID
INNER JOIN OrderDetails d
ON o.OrderID = d.OrderID
GROUP BY o.OrderID, DATEPART(YEAR,o.OrderDate), DATEPART(QUARTER,o.OrderDate),
E.FirstName + ' ' + E.LastName, C.CustomerName, S.ShipperName
OrderID OrderYear OrderQuarter EmployeeName CustomerName ShipperName Quantity
--------------------------------------------------------------------------------------------------------
10249 1996 3 Michael Suyama Tradicao Hipermercados Speedy Express 49
10250 1996 3 Margaret Peacocl Hanari Carnes United Package 60
10368 1996 4 Andrew Fuller Ernst Handel United Package 78
10389 1996 4 Margaret Peacock Bottom-Dollar Marketse United Package 81
10418 1996 1 Margaret Peacock QUICK-Stop Speedy Express 146
10442 1997 1 Janet Leaverling Ernst Handel United Package 170
Your syntax error was because you did not define a GROUP BY and reference the columns which you wanted to aggregate to. When you use an aggregate function (like SUM or COUNT) you must also tell the engine which columns you will be grouping by.

Related

SQL find the maximum value

I have 2 tables:
Customer table
CustomerId
FirstName
LastName
Country
1
Luís
Gonçalves
Brazil
2
Leonie
Köhler
Germany
3
François
Tremblay
Canada
4
Bjørn
Hansen
Norway
52
Emma
Jones
United Kingdom
53
Phil
Hughes
United Kingdom
Invoice table
InvoiceId
CustomerId
Total
1
2
1.98
2
4
3.96
3
8
5.94
140
52
23.76
369
52
13.86
283
53
28.71
109
53
8.91
I have to write a query that returns the country along with the top customer and how much they spent. For countries where the top amount spent is shared, provide all customers who spent this amount.
I wrote a query like:
SELECT c.CustomerId, c.FirstName, c.LastName, c.Country, SUM(i.Total) AS TotalSpent
FROM Customer c
JOIN Invoice i
ON c.CustomerId = i.CustomerId
GROUP BY c.FirstName, c.LastName
HAVING i.Total >= MAX(i.Total)
ORDER BY c.Country;
the query is not finding the maximum values but it is returning all available values.
I am not sure about which DBMS is used as it is my first steps in SQL coding and above example is from Udacity learning platform lab (Maybe it is SQLite that they are using in the lab)
Any help is appreciated. Thank you!
You did not shared your database with us.
Also, you need to add expected results in your question from the data you provided.
But lets say you use SQLite then this would work I think:
select CustomerId
, FirstName
, LastName
, Country
, max(tot)
from ( select sum(i1.Total) as tot
, i1.CustomerId
, c1.Country
, c1.FirstName
, c1.LastName
FROM Customer c1
JOIN Invoice i1
ON c1.CustomerId = i1.CustomerId
group by i1.CustomerId) TAB
group by Country
DEMO
After the comment from the OP I have edited the code:
select c.CustomerId, c.FirstName, c.LastName, c.Country, sum(Total)
from Customer c
JOIN Invoice i ON c.CustomerId = i.CustomerId
group by country, c.CustomerId
having sum(Total) in (select max(tot) as tot2
from (select sum(i1.Total) as tot
, country
FROM Customer c1
JOIN Invoice i1
ON c1.CustomerId = i1.CustomerId
group by i1.CustomerId) TAB
group by country)
DEMO2
As the accepted (at the time of writing this answer) solution would fail on at least PostgreSQL (for not including a selected value in either the group by clause or an aggregate function) I provide another variant:
WITH t AS
(
SELECT
c.customerid AS i, c.last_name AS l, c.first_name AS f,
c.country AS c, SUM(i.total) AS s
FROM customer c JOIN invoice i ON c.customerid = i.customerid
GROUP BY c.customerid, c.country
-- note: last and first name do not need to be included in the group by clause
-- here as the (assumed!) primary key 'customerid' already is
)
SELECT c, l, f, s FROM t
WHERE s = (SELECT MAX(s) FROM t WHERE t.c = c)
(tested on PostgreSQL)
Below code worked fine to fulfill all the requirements:
WITH tab1 AS
( SELECT c.CustomerId, c.FirstName, c.LastName,c.Country, SUM(i.Total) as TotalSpent
FROM Customer AS c
JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY C.CustomerId
)
SELECT tab1.*
FROM tab1
JOIN
( SELECT CustomerId, FirstName, LastName, Country, MAX(TotalSpent) AS TotalSpent
FROM tab1
GROUP BY Country
)tab2
ON tab1.Country = tab2.Country
WHERE tab1.TotalSpent = tab2.TotalSpent
ORDER BY Country;

Northwind - How many countries one shipper handled in one year

I need some help with my shipping query.
I need to count, how many different countries one shipping company delivered to in last year. My current query looks like this:
SELECT CompanyName, Count(o.CountryID) as Shipments, o.CountryID as Countries
FROM Shippers s
INNER JOIN Orders o ON s.ShipperID = o.ShipVia
WHERE DATEPART(year, o.OrderDate)=1997
GROUP BY CompanyName, o.CountryID
ORDER BY Shipments DESC;
It gives me list of Companies, How many times this company shipper to country with CountryID.
United Package 26 9
United Package 26 20
Speedy Express 23 9
Speedy Express 19 20
United Package 17 4
Speedy Express 16 4
What I need is to count how many distinctive countries one shipping company delivered to. So for example it should give me:
United Package 120 4
Speedy Express 90 3
United Package send 120 orders to 4 different countries.
How can I change my query to get that result?
SELECT CompanyName, SUM(Shipments) AS Shipments,COUNT(DISTINCT Countries) AS Countries
FROM
(
SELECT CompanyName, Count(o.CountryID) as Shipments, o.CountryID as Countries
FROM Shippers s
INNER JOIN Orders o ON s.ShipperID = o.ShipVia
WHERE DATEPART(year, o.OrderDate)=1997
GROUP BY CompanyName, o.CountryID
) AS T
GROUP BY CompanyName
ORDER BY Shipments DESC
I would recommend:
SELECT s.CompanyName, Count(*) as Shipments,
COUNT(DISTINCT o.CountryID) as Countries
FROM Shippers s INNER JOIN
Orders o
ON s.ShipperID = o.ShipVia
WHERE o.OrderDate >= '1997-01-01' AND o.OrderDate < '1998-01-01'
GROUP BY s.CompanyName
ORDER BY Shipments DESC;
Notes:
Qualify all column names, especially in a query that has more than one table reference.
Don't use datepart(). That prevents the use of indexes. Instead, do direct comparisons on dates.
COUNT(DISTINCT) appears to do what you want.

AdventureWorks2012:Identify the sales persons who have sales less than $100,000 since beginning of 2008. Show: Sales person login ID

AdventureWorks2012: Identify the sales persons who have sales less than $100,000 since beginning of 2008.
Show: Sales person login ID.
I guess the tables will be
HumanResources.Employee
Sales.SalesPerson
Can someone plz help me. I have spent enough time
SELECT
E.LoginID
FROM
HumanResources.Employee AS E
JOIN
Sales.SalesPerson AS SP ON E.BusinessEntityID = SP.BusinessEntityID
WHERE
E.BusinessEntityID IN (SELECT BusinessEntityID
FROM Sales.SalesPerson
WHERE SalesQuota < 100000);
You're close - but you're not really looking at the sales that each sales person has had over the past years... The SalesQuota column that you're looking at in the SalesPerson table is usually a figure that is defined as a goal for the year - it's not an actual sum of all sales made.
I would try something like this:
-- determine the SUM of all sales, for all salespeople, since Jan 1, 2008
-- SUM the total of all sales (their TotalDue value) from the SalesOrderHeader table
;WITH SalesPerPerson AS
(
SELECT
sp.BusinessEntityID,
TotalSales = SUM(soh.TotalDue) -- SUM of all sales total amounts
FROM
Sales.SalesOrderHeader soh
INNER JOIN
Sales.SalesPerson sp ON soh.SalesPersonID = sp.BusinessEntityID
WHERE
soh.OrderDate >= '20080101' -- on all sales since Jan 1, 2008
GROUP BY
sp.BusinessEntityID
)
-- from that CTE, select the sales people who have had less than 100'000$ in
-- sales since Jan 1, 2008, and display those. Show LoginID and first and
-- last name (just for information purposes)
SELECT
p.FirstName, p.LAstName,
e.LoginID,
spp.*
FROM
SalesPerPerson spp
INNER JOIN
Person.Person p ON spp.BusinessEntityID = p.BusinessEntityID
INNER JOIN
HumanResources.Employee e ON p.BusinessEntityID = e.BusinessEntityID
WHERE
TotalSales < 100000.0
So if you really only need the LoginID, you can leave out the JOIN to the Person.Person table and remove the p.FirstName, p.LastName columns from the output.

Need assistance with basic sql query

I was doing some exercises for practice and have a small problem with my query.
My initial code is below. The problem is I am getting the wrong answer in the final column/
The question : a query to find out the total attributed sales to each sales person for all SalesOrders placed in 2008. Use the TotalDue of the orders to find the sales amounts and the SalesPersonID to find the sales person to attribute. E.g. SalesOrderID 43659 should be attributed to SalesPersonID 279.
SELECT DISTINCT p.BusinessEntityID,p.FirstName,p.LastName, t.TotalDue
FROM Person.Person p,Sales.SalesPerson s,Sales.SalesOrderHeader t
WHERE p.BusinessEntityID = s.BusinessEntityID
Here is the image of my ERD http://www.2shared.com/photo/h2-jMQyP/AdventureWorks2008.html .
This looks like the ideal example of using the GROUP BY clause.
Try the following query:
select p.BusinessEntityID, p.FirstName, p.LastName, sum(t.TotalDue) as TotalDue
from Person.Person p
join Sales.SalesPerson s on p.BusinessEntityID = s.BusinessEntityID
join Sales.SalesOrderHeader t on t.BusinessEntityID = s.BusinessEntityID
where year(t.OrderDate) = 2008
group by p.BusinessEntityID, p.FirstName, p.LastName
if you want totaldue made in 2008 you have to give one more condition for this year part
SELECT DISTINCT p.BusinessEntityID,p.FirstName,p.LastName, t.TotalDue
FROM Person.Person p,Sales.SalesPerson s,Sales.SalesOrderHeader t
WHERE p.BusinessEntityID = s.BusinessEntityID
and year(date_column) = '2008'

TSQL basic join on Northwind database

I am learning TSQL (well, just SQL to tell the truth) and I want to make Employee - Product statistic on Northwind database.
Expected results should be something like:
EmployeeID | ProductID | income
1 | 1 | 990
1 | 2 | 190
1 | 3 | 0
...
For all Employy-Product pairs
My first try is this query:
SELECT E.EmployeeID, OE.ProductID, SUM(OE.ExtendedPrice) as income
FROM [Order Details Extended] OE
JOIN [Orders] O
ON OE.OrderID = O.OrderID
RIGHT OUTER JOIN Employees E
ON E.EmployeeID = O.EmployeeID
GROUP BY E.EmployeeID, OE.ProductID
ORDER BY E.EmployeeID
But I don't get results for all pairs.
What am I doing wrong?
HLGEM missed few columns but I understood what he tried to do.
I came up with this:
SELECT A.employeeid, A.productid, SUM(Oe.ExtendedPrice) AS income
FROM
(SELECT E.Employeeid, P.productid
FROM employees E
CROSS JOIN products P) A
LEFT JOIN [Order Details Extended] OE
ON A.productid = OE.productid
LEFT JOIN [Orders] O
ON OE.OrderID = O.OrderID
GROUP BY A.EmployeeID, A.ProductID
ORDER BY A.EmployeeID, A.ProductID
This returns results for all pairs, but those don't seem right.
For example above query returns as first row:
1, 1, 12788.10
But this query:
SELECT SUM(ODE.ExtendedPrice) FROM [Order Details Extended] ODE
LEFT JOIN [Orders] OD
ON ODE.OrderID = OD.OrderID
WHERE OD.EmployeeID = 1 AND ODE.ProductID = 1
Returns 990.90.
Why?
edit
I got it finally:
SELECT A.EmployeeId, A.ProductId, ISNULL(SUM(Oe.ExtendedPrice), 0) AS income
FROM
(SELECT E.Employeeid, P.productid
FROM [Employees] E
CROSS JOIN [Products] P) A
LEFT JOIN [Orders] O
ON O.EmployeeID = A.EmployeeID
LEFT JOIN [Order Details Extended] OE
ON A.productid = OE.productid AND OE.OrderID = O.OrderID
GROUP BY A.EmployeeID, A.ProductID
ORDER BY A.EmployeeID, A.ProductID
#HLGEM you can copy/paste this solution to your answer so I can accept it.
You could try:
SELECT A.employeeid,A.product_id, SUM(Oe.ExtendedPrice) AS income
FROM
(SELECT E.Employeeid, P.product id
FROM employee E
CROSS JOIN product p) A
LEFT JOIN [Order Details Extended] OE
ON A.EmployeeID = O.EmployeeID
LEFT JOIN [Orders] O
ON OE.OrderID = O.OrderID
GROUP BY A.EmployeeID, OE.ProductID
ORDER BY A.EmployeeID
I switched it to a LEFT JOIN as most people use them instead of right joins and thus they are easier for maintenance.
What results are you missing? It looks like you have used an inner join between Order Details Extended and Orders, so any orders that don't have details will be excluded. It seems logical that you would want this, since you are summing a value in the details table.
Then you do a right outer join with Employees, so you are including all employees, regardless of whether they have any orders. This also makes sense, since it looks as though you are seeing which employees sold which products.
Your query will only give you the data for products that the employees have actually sold. If you are looking for all employee-product combinations, regardless of whether the employee had ever sold that product, you will want to join the Product table as well, although in my muddled morning, I can't think if you would want an outer join or a cross join.
In all, I think your first try is a good one. Pretty complex for a beginner. Nice effort!