如何与聚合函数一起选择列?

时间:2012-06-22 18:53:36

标签: sql-server aggregate-functions

假设我有一张公司表:

Company
coID | coName | coCSR

coCSR字段是与帐户处理程序表相关的数字ID:

AccountHandler
ahID | ahFirstName | ahLastName

我还有一张订单表:

Order
orID | orCompanyID | orDate | orValue

现在我需要生成的输出结构如下:

Company | Account handler | No. of orders | Total of orders

这是我尝试过的查询,产生错误:

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

错误是:列名'AccountHandler.ahLastName'在ORDER BY子句中无效,因为它不包含在聚合函数中且没有GROUP BY子句。

如果我使用GROUP BY coID,我会在关键字'WHERE'附近获得不正确的语法。如果由于聚合函数我将WHERE更改为HAVING ,我收到错误告诉我删除聚合函数或GROUP BY子句中未包含的每个其他列名。

我不得不承认,除了最基本的SQL命令之外,我还没有理解任何逻辑和语法,我只是试图应用我以前见过的东西,而且它不起作用。请帮我搞定这个。更好的是,你能帮助我理解为什么它现在不起作用吗? :)

3 个答案:

答案 0 :(得分:5)

首先,您的查询可能缺少FROM Company,但在撰写帖子时可能会以某种方式丢失。

您似乎正在通过公司汇总数据。因此,您需要按公司分组。您尝试分组失败的最可能原因可能是您将GROUP BY置于错误的位置。我认为你把它放在WHERE之前,但事实上它应该追求它(在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

另一个问题是,要分组的。 SQL Server要求在GROUP BY中指定所有非聚合列。因此,您的GROUP BY子句应如下所示:

GROUP BY
  c.coID,
  c.coName,
  a.ahFirstName,
  a.ahLastName

请注意,您无法通过SELECT子句中指定的别名引用列(例如CSRName)。但是你可以使用ahFirstName+' '+ahLastName表达式而不是相应的列,在这种特殊情况下它不会有任何区别。

如果您需要向此查询添加更多非聚合列,则必须将它们添加到SELECTGROUP BY。在某些时候,这可能会变得有点单调乏味。我建议你尝试以下方法:

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

也就是说,聚合只在Order表上完成。然后,聚合的行集将连接到其他表。您现在可以从CompanyAccountHandler向输出中提取更多属性,而无需担心将其添加到GROUP BY,因为不再需要在该级别进行分组。

答案 1 :(得分:1)

您可以更改下面的查询吗?您应该添加MaxGroup 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

我的建议

您应该为选定的列名称使用别名

答案 2 :(得分:0)

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

你有两个聚合函数; COUNTSUM;这意味着您需要进行一些分组,一般的经验法则是GROUP BY select子句的非聚合部分

你的OP中一个非常大的问题是,当你加入两个表时,无论什么风格(LEFT,RIGHT,OUTER,INNER,CROSS)都必须在FROM子句中,并且需要在连接的任一侧指定一个表

然后,如果连接多个表,您可能希望为每个表使用别名;我刚刚使用过单个小写字母; C / O / A。虽然查看列名称可能不需要,但所有列都是唯一命名的。