This question already has answers here:
Reason for Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause [duplicate]
(4 answers)
Closed 11 months ago.
I have two TABLES as customers$ and orders$ (I am not sure why there is a $ sign after the table name. It appears in my Microsoft SQL Server Management Studio every time I import the excel file).
customer$ table has 2 columns = name(nvarchar(255,null) | customer_id(float,null)
orders$ table has 4 columns = order_id(nvarchar(255,null) | customer_id | status(nvarchar(255,null) | order_date(datetime, null)
My statement is to count the number of orders per customer
SELECT c.name,c.customer_id AS CustomerID,o.customer_id AS OcustomerID, COUNT(*) AS Number_of_orders
FROM customers$ c, orders$ o
WHERE c.customer_id = o.customer_id
GROUP BY name
ORDER BY Number_of_orders
OUTPUT:
Msg 8120, Level 16, State 1, Line 1
Column 'customers$.customer_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
When you use an aggregate function (like COUNT, SUM, AVG, etc.), any field that is not aggregated must be listed in the GROUP BY clause:
SELECT c.name, c.customer_id AS CustomerID, COUNT(*) AS Number_of_orders
FROM customers$ c
INNER JOIN orders$ o ON c.customer_id = o.customer_id
WHERE c.customer_id = o.customer_id
GROUP BY c.name, c.customer_id
ORDER BY Number_of_orders
You also don't need o.customer_id. The join condition make sure that c.customer_id and o.customer_id are identical.
Also, avoid the old join syntax:
FROM tableA, tableB
WHERE tableA.column = tableB.column
Replace it with INNER JOIN:
FROM tableA
INNER JOIN tableB ON tableA.column = tableB.column
You probably want to do either an in-line select
SELECT c.name,c.customer_id AS CustomerID, (select COUNT(*) FROM orders$ o c.customer_id = o.customer_id) AS Number_of_orders
ORDER BY Number_of_orders
or you need to add c.customer_id to your Group By. Also no point in selecting o.Customer_id as that should be identical to c.customer_id
SELECT c.name,c.customer_id AS CustomerID, COUNT(*) AS Number_of_orders
FROM customers$ c, orders$ o
WHERE c.customer_id = o.customer_id
GROUP BY name, c.customer_id
ORDER BY Number_of_order
Related
I have a little problem with some query.
This is the task:
Create a query that displays the employees with no sale in the last 3 months to customers who are from "USA".
This is what i wrote:
Select emp.EmployeeID, (emp.FirstName + ' ' + emp.LastName) AS Name
From Employees AS emp
Join Orders AS o ON emp.EmployeeID = o.EmployeeID
Join Customers AS c ON o.CustomerID = c.CustomerID
Where c.Country LIKE 'USA';
One of the problem is that i don't know where to put this select query (it's for calculating the last 3 months but i'm not sure that this is true):
Select DATEDIFF(MM, '1998-02-01', '1998-05-31') From Orders
The second problem is that i don't have an idea for the part "employees with no sale" How can i find this?
Should i use other kind of joins or something else?
Sorry for my question but i'm new in SQL and i'll appreciate any kind of help.
If you have any questions, please ask. :)
Create a query that displays the employees with no sale in the last 3 months to customers who are from "USA"
Select * From Employees e
Where not exists
(Select * from orders o join customers c
on c.CustomerID = o.CustomerID
Where c.Country = "USA"
and o.saleDate >= DateAdd(month, -3, getdate()))
How you actually treat "last 3 months" depends on you, but this is one way to approach the task:
select e.*
from employees e
where not exists (
select 1
from orders o
join customers c on c.customerid = o.customerid
where e.employeeid = o.employeeid
and c.country = 'USA'
and c.orderdate > dateadd(month, datediff(month, 0, getdate()), 3)
);
Last three months could mean months per se or just days that make exactly last three months (eg. 91)
If you're just learning SQL, please don't take one of these answers and use it to do your homework. You'll need to figure out what's going on for yourself.
Try to take this task in pieces. First, let's look at 'customers from USA.' You can probably write this one yourself:
Select * from customers where country = 'usa'
Next, consider how to find the orders for those customers (all of them, for now).
Since you've posted an example of an inner join, I'm going to assume you could write that one yourself, too:
Select o.* from customers c inner join orders o on
c.customerID = o.customerID
where country = 'usa'
Now you've asked where/how to apply the criteria for 'sales in the last 3 months.' Note each order has an orderDate, representing the date the order was placed. You need to use the order table's orderdate field with today's date and compare the number of months between them. The getdate() function returns the date and time from the server. Try executing:
select getdate()
then you can experiment with the datediff() function on the orderDates in the orders table:
Select orderid, getdate(), orderdate, datediff(mm,orderdate,getdate())
from orders
I think you'll have better luck, though, adding 3 months to orderdate and comparing with getdate():
Select orderid, orderdate, dateadd(month, orderdate, 3),
getdate(), dateadd(month, getdate(), -3)
from orders
Now you can see all the employee IDs you don't want, the ones that have orders within the last 3 months, and you already know how to limit orders by customers in USA. Those are the IDs you want to exclude from the employees table. You can do that in a couple of different ways, depending on what you've learned so far, but typically you're exposed to LEFT OUTER JOINs for this sort of thing. You'll want to left outer join your employee table with the set of IDs you don't want (the ones with orders in the past 3 months) on the employeeID field, and return rows where the employeeID from the order subquery is null.
Try:
select count(*) from employees -- note rowcount
then:
select min(employeeID) from orders -- pick one employee
then:
select count(e.employeeID)
from employees e left outer join
(select * from orders
where employeeID in (select min(employeeID) from orders)
) o on e.employeeID = o.employeeID
where o.employeeID is null
this count should be one less than the total number of rows in your employees table, it should exclude the lowest employeeID with an order.
Then see if you can figure out how to do your homework.
Left/Right joins exist as well and you can somewhat think of them as Venn diagrams (Join or Inner Join is the intersect, Left Join is intersect and left circle, etc.). Its one perspective on how to think what will be returned. Fields returned which are not in the intersect like with a Left Join will be NULL.
The below is another way to do it:
Select emp.EmployeeID, (emp.FirstName + ' ' + emp.LastName) AS Name
From Employees AS emp
Left Join Orders As o ON emp.EmployeeID = o.EmployeeID
And o.OrderDate > DateAdd(month,-3,GetDate())
Join Customers AS c ON o.CustomerID = c.CustomerID
Where c.Country = 'USA' And o.EmployeeID is Null
Group By emp.EmployeeID, emp.FirstName, emp.LastName;
Question
I have a query and would like to join one column from another table based on its matching ID
Query So far
SELECT DENumber, AcquiredDate, ItemDescription, ItemName, LocationID FROM dbo.Assets JOIN LocationName FROM dbo.Locations on ProductID
WHERE DATEDIFF(YEAR, AcquiredDate, GetDate()) >= 6
The matching ID in both tables is LocationID
Problem
My query is wrong and throws errors
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'FROM'.
Rewrite your query to this:
SELECT DENumber, AcquiredDate, ItemDescription, ItemName, LocationName
FROM dbo.Assets INNER JOIN dbo.Locations ON Assets.LocationId = Locations.LocationId
WHERE DATEDIFF(YEAR, AcquiredDate, GetDate()) >= 6
To clarify: In a JOIN, you must specify the names of both tables on either side of the JOIN keyword. Then, you specify the join criteria after the ON keyword:
FROM <<table1>> INNER JOIN <<table2>> ON <<join criteria>>
Here, we only want records that exist in both tables, which is why we use the INNER join, but you can also take all records from either the LEFT table, the RIGHT table or BOTH tables. In that case, you would do a LEFT JOIN, RIGHT JOIN our OUTER JOIN respectively.
SELECT L.DENumber, A.AcquiredDate, A.ItemDescription, A.ItemName, L.LocationID
FROM dbo.Assets A JOIN
ON dbo.Locations L on
L.ProductID = A.ProductID
WHERE DATEDIFF(YEAR, A.AcquiredDate, GetDate()) >= 6
I was trying to write a query for the SQL Server sample DB Northwind. The question was: "Show the most recent five orders that were purchased by a customer who has spent more than $25,000 with Northwind."
In my query the Alias name - "Amount" is not being recognized. My query is as follows:
select top(5) a.customerid, sum(b.unitprice*b.quantity) as "Amount", max(c.orderdate) as Orderdate
from customers a join orders c
on a.customerid = c.customerid
join [order details] b
on c.orderid = b.orderid
group by a.customerid
--having Amount > 25000 --throws error
having sum(b.unitprice*b.quantity) > 25000 --works, but I don't think that this is a good solution
order by Orderdate desc
Pls let me know what I am doing wrong here, as I am a newbie in writing T Sql. Also can this query and my logic be treated as production level query?
TIA,
You must use the aggregate in the query you have. This all has to do with the order in which a SELECT statement is executed. The syntax of the SELECT statement is as follows:
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
The order in which a SELECT statement is executed is as follows. Since the SELECT clause isn't executed until after the HAVING clause, you can't use the alias like you can in the ORDER BY clause.
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
Reference Article: http://www.bennadel.com/blog/70-sql-query-order-of-operations.htm
This is a known limitation in SQL Server, at least, but no idea if it's a bug, intentional or even part of the standard. But the thing is, neither the WHERE or HAVING clauses accept an alias as part of their conditions, you must use only columns from the original source tables, which means that for filtering by calculated expressions, you must copy-paste the very same thing in both the SELECT and WHERE parts.
A workaround for avoiding this duplication can be to use a subquery or cte and apply the filter on the outer query, when the alias is just an "input" table:
WITH TopOrders AS (
select a.customerid, sum(b.unitprice*b.quantity) as "Amount", max(c.orderdate) as Orderdate
from customers a join orders c
on a.customerid = c.customerid
join [order details] b
on c.orderid = b.orderid
group by a.customerid
--no filter here
order by Orderdate desc
)
SELECT TOP(5) * FROM TopOrders WHERE Amount > 25000 ;
Interesting enough, the ORDER BY clause does accepts aliases directly.
You must use Where b.unitprice*b.quantity > 25000 instead of having Amount > 25000.
Having used for aggregate conditions. Your business determine your query condition. If you need to calculate sum of prices that have above value than 25000, must be use Where b.unitprice*b.quantity > 25000 and if you need to show customer that have total price above than 25000 must be use having Amount > 25000 in your query.
select top(5) a.customerid, sum(b.unitprice*b.quantity) as Amount, max(c.orderdate) as Orderdate
from customers a
JOIN orders c ON a.customerid = c.customerid
join [order details] b ON c.orderid = b.orderid
group by a.customerid
having sum(b.unitprice*b.quantity) > 25000 --works, but I don't think that this is a good solution
Order by Amount
I don't have that schema at hand, so table' and column' names might go a little astray, but the principle is the same:
select top (5) ord2.*
from (
select top (1) ord.CustomerId
from dbo.Orders ord
inner join dbo.[Order Details] od on od.OrderId = ord.OrderId
group by ord.CustomerId
having sum(od.unitPrice * od.Quantity) > $25000
) sq
inner join dbo.Orders ord2 on ord2.CustomerId = sq.CustomerId
order by ord2.OrderDate desc;
The Having Clause will works with aggregate function like SUM,MAX,AVG..
You may try like this
SELECT TOP 5 customerid,SUM(Amount)Amount , MAX(Orderdate) Orderdate
FROM
(
SELECT A.customerid, (B.unitprice * B.quantity) As "Amount", C.orderdate As Orderdate
FROM customers A JOIN orders C ON A.customerid = C.customerid
JOIN [order details] B ON C.orderid = B.orderid
) Tmp
GROUP BY customerid
HAVING SUM(Amount) > 25000
ORDER BY Orderdate DESC
The question is little ambiguos.
Show the most recent five orders that were purchased by a customer who
has spent more than $25,000 with Northwind.
Is it asking to show the 5 recent orders by all the customers who have spent more than $25,000 in all of their transactions (which can be more than 5).
The following query shows all the customers who spent $25000 in all of their transactions (not just the recent 5).
In one of the Subquery BigSpenders it gets all the Customers who spent more than $25000.
Another Subquery calculates the total amount for each order.
Then it gets rank of all the orders by OrderDate and OrderID.
Then it filters it by Top 5 orders for each customer.
--
SELECT *
FROM (SELECT C.customerid,
C.orderdate,
C.orderid,
B3.amount,
Row_number()
OVER(
partition BY C.customerid
ORDER BY C.orderdate DESC, C.orderid DESC) Rank
FROM orders C
JOIN
--Get Amount Spend Per Order
(SELECT b2.orderid,
Sum(b2.unitprice * b2.quantity) AS Amount
FROM [order details] b2
GROUP BY b2.orderid) B3
ON C.orderid = B3.orderid
JOIN
--Get Customers who spent more than 25000
(SELECT c.customerid
FROM orders c
JOIN [order details] b
ON c.orderid = b.orderid
GROUP BY c.customerid
HAVING Sum(b.unitprice * b.quantity) > 25000) BigSpenders
ON C.customerid = BigSpenders.customerid) X
WHERE X.rank <= 5
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
I want to count the total number of order detail rows over all orders a customer has ever had.
This is my query
SELECT SUM(
(SELECT count(*)
FROM dbo.Order_Details
WHERE dbo.Order_Details.OrderID = dbo.Orders.OrderID))
FROM dbo.Orders
WHERE dbo.Orders.CustomerID = "123"
SQL Server is giving me an error "Cannot perform an aggregate function on an expression containing an aggregate or a subquery."
Any help with this would be appreciated.
SELECT COUNT(*)
FROM Orders
INNER JOIN Order_Details ON Orders.OrderID = Order_Details.OrderID
WHERE Orders.CustomerID = "123"
Shouldn't it just be:
SELECT count(*) FROM dbo.Order_Details, dbo.Orders
WHERE dbo.Order_Details.OrderID = dbo.Orders.OrderID
AND dbo.Orders.CustomerID = "123"
You don't need the sum() since the count(*) is already going to give you the total.
SELECT (SELECT count(*)
FROM dbo.Order_Details
WHERE dbo.Order_Details.OrderID = dbo.Orders.OrderID)
FROM dbo.Orders
WHERE dbo.Orders.CustomerID = "123"
The Count(*) is doing the summation for you. Just remove the SUM aggregate from your expression.
I should think something like the following should do what you want:
select count(1) from dbo.order_details d
join dbo.orders o on d.OrderId=o.OrderId
where dbo.orders.CustomerID="123"
The following assumes you have a column in the Order_Details table called OrderDetailID. If not, just substitute for the unique identifier for the order detail record.
SELECT COUNT(DISTINCT OD.OrderDetailID)
FROM Orders O
LEFT JOIN Order_Details OD on (OD.OrderId = O.OrderId)
WHERE O.CustomerID = "123"