Reputation: 2302
Let's say I have a table of companies:
Company
coID | coName | coCSR
The coCSR
field is a numeric ID which relates to the account handler table:
AccountHandler
ahID | ahFirstName | ahLastName
I also have a table of orders:
Order
orID | orCompanyID | orDate | orValue
Now what I need to produce is output structured as follows:
Company | Account handler | No. of orders | Total of orders
Here is the query I have tried, which produces an error:
SELECT coID, coName, ahFirstName+' '+ahLastName AS CSRName, COUNT(orID) AS numOrders, SUM(orValue) AS totalRevenue
FROM Company
LEFT JOIN AccountHandler ON coCSR = ahID
LEFT JOIN Order ON coID = orCompanyID
WHERE coCSR = 8
AND orDate > getdate() - 365
ORDER BY coName ASC
The error is: Column name 'AccountHandler.ahLastName' is invalid in the ORDER BY clause because it is not contained in an aggregate function and there is no GROUP BY clause.
If I use GROUP BY coID
, I get Incorrect syntax near the keyword 'WHERE'. If I change the WHERE
to HAVING
because of the aggregate functions, I get errors telling me to remove each of the other column names that aren't contained in either an aggregate function or the GROUP BY clause.
I have to admit, I don't yet understand the logic and syntax of anything but the most basic SQL commands, I'm just trying to apply what I've seen used before, and it's not working. Please help me to get this working. Better still, can you help me understand why it doesn't work at the moment? :)
Upvotes: 6
Views: 43328
Reputation: 77707
For one thing, your query is probably missing FROM Company
, but that might somehow have been lost when you were writing your post.
You seem to be aggregating data by companies. Therefore you need to group by companies. The most likely reason why your attempt at grouping failed might be because you put GROUP BY
in the wrong place. I think you put it before WHERE
, but in fact it should go after it (and before ORDER BY
):
SELECT
c.coID,
c.coName,
a.ahFirstName + ' ' + a.ahLastName AS CSRName,
COUNT(o.orID) AS numOrders,
SUM(o.orValue) AS totalRevenue
FROM Company c
LEFT JOIN AccountHandler a ON c.coCSR = a.ahID
LEFT JOIN [Order] o ON c.coID = o.orCompanyID
WHERE c.coCSR = 8
AND o.orDate > getdate() - 365
GROUP BY ...
ORDER BY c.coName ASC
Another question is, what to group by. SQL Server requires that all non-aggregated columns be specified in GROUP BY
. Therefore your GROUP BY
clause should look like this:
GROUP BY
c.coID,
c.coName,
a.ahFirstName,
a.ahLastName
Note that you can't reference columns by aliases assigned to them in the SELECT
clause (e.g. CSRName
). But you could use the ahFirstName+' '+ahLastName
expression instead of the corresponding columns, it wouldn't make any difference in this particular situation.
If you ever need to add more non-aggregated columns to this query, you'll have to add them both to SELECT
and to GROUP BY
. At some point this may become a bit tedious. I would suggest you try the following instead:
SELECT
c.coID,
c.coName,
a.ahFirstName + ' ' + a.ahLastName AS CSRName,
ISNULL(o.numOrders, 0) AS numOrders,
ISNULL(o.totalRevenue, 0) AS totalRevenue
FROM Company c
LEFT JOIN AccountHandler a ON c.coCSR = a.ahID
LEFT JOIN (
SELECT
orCompanyID,
COUNT(orID) AS numOrders,
SUM(orValue) AS totalRevenue
FROM [Order]
GROUP BY
orCompanyID
WHERE orDate > getdate() - 365
) o ON c.coID = o.orCompanyID
WHERE c.coCSR = 8
ORDER BY c.coName ASC
That is, aggregating is done on the Order
table only. The aggregated row set is then joined to the other tables. You can now pull more attributes to the output from either Company
or AccountHandler
without worrying about adding them to GROUP BY
because grouping is not needed at that level any more.
Upvotes: 5
Reputation: 35577
SELECT
--<<<non aggregate section of SELECT clause
coID
, coName
, [CSRName] = CONVERT(VARCHAR(100),ahFirstName + ' ' + ahLastName)
--<<<aggregate section of SELECT clause
, [numOrders] = COUNT(orID)
, [totalRevenue] = SUM(orValue)
FROM --<<<<<<sql is not too happy without FROM
Company c
LEFT JOIN AccountHandler a
ON coCSR = ahID
LEFT JOIN Order o
ON coID = orCompanyID
WHERE coCSR = 8
AND orDate > getdate() - 365
GROUP BY
coID
, coName
, CONVERT(VARCHAR(100),ahFirstName + ' ' + ahLastName) --<<<<looks like aggregate but is just text manipulation
ORDER BY coName ASC
You have two aggregate functions ; a COUNT
and a SUM
; this means that you are required to do some grouping and the general rule of thumb is GROUP BY
the non aggregate section of the select clause
The really big problem in your OP is that when you JOIN two tables, whatever flavour (LEFT, RIGHT, OUTER, INNER, CROSS) it has to be in the FROM clause and needs a table specified on either side of the join
Then if joining several tables you might like to use aliases for each of the tables; I've just used single lower case letter; c/o/a. Although looking at your column names these might not be needed as all columns are uniquely named.
Upvotes: 0
Reputation: 10105
Can you change the query like below? You should add Max
and Group By Clause
SELECT
MAX(C.coID),
C.coName,
MAX(AH.ahFirstName+' '+ AH.ahLastName ) AS CSRName,
COUNT(O.orID) AS numOrders,
SUM(O.orValue) AS totalRevenue
From Company C
LEFT JOIN AccountHandler AH ON C.coCSR = AH.ahID
LEFT JOIN Order O ON C.coID = O.orCompanyID
WHERE C.coCSR = 8 AND
O.orDate > getdate() - 365
Group by C.coName
ORDER BY C.coName ASC
You should use Alias for the selected Column Names
Upvotes: 1