SQL Server 2008 - “Msg 8120”的错误错误?

时间:2017-12-06 17:38:10

标签: sql-server sql-server-2008 ssms common-table-expression

我正在SQL Server 2008中编写一个查询(我相信Express吗?)。我目前收到此错误:

  

Msg 8120,Level 16,State 1,Line 16
  列'AIM.dbo.AggTicket.TotDirectHrs'在选择列表中无效,因为它不包含在聚合函数或GROUP BY子句中。

我正在尝试对我们的生产WIP(在制品)进行历史分析。 我创建了一个独立的日历表(实际上位于同一服务器上名为BAS的单独数据库中,不会干扰运行AIM数据库的ERP)。我已经被一些用于创建运行总查询/视图/表的示例所淹没了几天,所以现在我只打算在Crystal Reports 2016中处理该部分。我的想法是我想要返回我的日历表每天每个订单的记录(将来缩小到仅与AIM数据库中的记录匹配的天数)。我认为我需要的价值观是:

  • 记录日期(不是唯一的)
  • 订单号(每天唯一)
  • 预计工作时间
  • 截至今天的工作当前工作小时数(如果预计工作时间大幅减少)
  • 在上述记录日期收取工作的直接人工小时数的总和
  • 在上述记录日期出席的员工人数的COUNT。
  • 员工在上述记录日期参加的小时数的总和。

我使用的表格如下:

BAS数据库:

  • dbo.DateDimension - 用于完整的日历,从1987年1月1日到12月31日

AIM数据库:

  • dbo.AggAttend - 包含每个员工在给定日期的出勤时间的一个或多个记录(即每次打卡/打卡的一个记录。应该等于间接+直接人工)
  • dbo.AggTicket - 包含每个员工对特定订单号收取的直接人工期的一个或多个记录
  • dbo.ModOrders - 包含每个订单的一条记录,包括估计的小时数,开始日期和结束日期(我将担心稍后使用开始日期和结束日期来确定每个日期的可用小时数)< / LI>

以下是我在查询中使用的代码:

;WITH OrderTots AS
(
    SELECT
        AggTicket.OrderNo,
        SUM(AggTicket.TotDirectHrs) AS TotActHrs
    FROM
        AIM.dbo.AggTicket
    GROUP BY
        AggTicket.OrderNo
)
SELECT
    d.Date,
    t.OrderNo,
    o.EstHrs,
    OrderTots.TotActHrs,
    SUM(t.TotDirectHrs) OVER (PARTITION BY t.TicketDate) AS DaysDirectHrs,
    COUNT(a.EmplCode) AS NumEmployees,
    SUM(a.TotHrs) AS DaysAttendHrs
FROM
    BAS.dbo.DateDimension d
INNER JOIN 
    AIM.dbo.AggAttend a ON d.Date = a.TicketDate
LEFT OUTER JOIN 
    AIM.dbo.AggTicket t ON d.Date = t.TicketDate
LEFT OUTER JOIN 
    AIM.dbo.ModOrders o ON t.OrderNo = o.OrderNo
LEFT OUTER JOIN 
    OrderTots ON t.OrderNo = OrderTots.OrderNo
GROUP BY
    d.Date, t.TicketDate, t.OrderNo, o.EstHrs,
    OrderTots.TotActHrs
ORDER BY
    d.Date

当我在SQL Server Management Studio 2017中运行该查询时,出现上述错误。

这些是我对社区的问题:

  1. 此错误消息是否正确描述了我的代码中的错误?
  2. 如果是这样,为什么该错误是错误的? (据我所知,一切都已包含在聚合函数或GROUP BY子句中...... smh)
  3. 编写此查询以使其正常运行的更好方法是什么?
  4. 提前感谢所有人!

1 个答案:

答案 0 :(得分:0)

  

我正在SQL Server 2008中编写一个查询(我相信Express吗?)。

SELECT @@VERSION会让你知道你的版本。

  

专栏&#39; AIM.dbo.AggTicket.TotDirectHrs&#39;在选择列表中无效   因为它不包含在聚合函数或   GROUP BY子句。

问题在于您的SUM OVER()声明:

SUM(t.TotDirectHrs) OVER (PARTITION BY t.TicketDate) AS DaysDirectHrs

此处,由于您使用的是OVER子句,因此必须将其包含在GROUP BY中。 OVER子句用于确定窗口函数的行集的分区和顺序。因此,当您使用SUM聚合时,您正在窗口函数中执行此操作。窗口函数属于一种称为“设置函数”的函数,它表示适用于一组行的函数。单词&#39; window&#39;用于指代函数处理的行集。

因此,将t.TotDirectHrs添加到GROUP BY

GROUP BY
    d.Date, t.TicketDate, t.OrderNo, o.EstHrs,
    OrderTots.TotActHrs, t.TotDirectHrs

如果这会将您的结果缩小为您不想要的分组,那么您可以将其包装在另一个CTE中或使用相关的子查询。 潜在如下所示:

(SELECT SUM(t2.TotDirectHrs) OVER (PARTITION BY t2.TicketDate) AS DaysDirectHrs FROM  AIM.dbo.AggTicket t2 WHERE t2.TicketDate = t.TicketDate) as DaysDirectHrs,

示例

if object_id('tempdb..#test') is not null
drop table #test

create table #test(id int identity(1,1), letter char(1))
insert into #test
values
('a'),
('b'),
('b'),
('c'),
('c'),
('c')

鉴于上面的数据集,假设我们想要获得所有行的计数。这很简单吧?

select 
    TheCount = count(*) 
from 
    #test

+----------+
| TheCount |
+----------+
|        6 |
+----------+

此处不需要GROUP BY,因为它隐含在所有列上进行分组,因为SELECT列表中未指定任何列。请记住,GROUP BY 根据一个或多个列表达式列表中的值对SELECT语句结果进行分组。如果聚合函数包含在SELECT列表中,GROUP BY将计算每个组的汇总值。这些被称为矢量聚合。[MSDN]

现在,假设我们想要计算表格中的每个字母。我们至少可以通过两种方式做到这一点。将COUNT(*)与选择列表中的letter列一起使用 - 或将COUNT(letter)letter列一起使用在选择列表中。但是,为了让我们将计数归入字母,我们需要返回字母列。因此,我们必须在letter中包含GROUP BY,告诉SQL Server将摘要表应用到什么位置。

select 
    letter
    ,TheCount = count(*) 
from 
    #test
group by
    letter

+--------+----------+
| letter | TheCount |
+--------+----------+
| a      |        1 |
| b      |        2 |
| c      |        3 |
+--------+----------+

现在,如果我们想要返回相同的计数,但我们想要返回所有行呢?这就是窗口函数的用武之地。在这种情况下,窗口函数GROUP BY的工作方式类似,告诉SQL Server应用聚合的行集。然后,为此窗口/分区中的每一行返回它的值。因此,它返回一个应用于每一行的列,使其与从选择列表返回的任何列或计算列一样。

select
    letter
    ,TheCountOfTheLetter = count(*) over (partition by letter)
from 
    #test

+--------+---------------------+
| letter | TheCountOfTheLetter |
+--------+---------------------+
| a      |                   1 |
| b      |                   2 |
| b      |                   2 |
| c      |                   3 |
| c      |                   3 |
| c      |                   3 |
+--------+---------------------+

现在我们来看一下你想要在窗口函数中使用聚合和聚合的情况。请记住,窗口函数的返回与任何其他列一样,因此必须应用于GROUP BY。 Pseudo看起来像这样,但GROUP BY子句中不允许使用窗口函数。

select 
    letter
    ,TheCount = count(*) 
    ,TheCountOfTheLetter = count(*) over (partition by letter)
from 
    #test
group by
    letter
    ,count(*) over (partition by letter)

--returns an error

因此,我们必须使用相关的子查询或cte或其他方法。

select 
    t.letter
    ,TheCount = count(*) 
    ,TheCountOfTheLetter = (select distinct count(*) over (partition by letter) from #test t2 where t2.letter = t.letter)
from 
    #test t
group by
    t.letter

+--------+----------+---------------------+
| letter | TheCount | TheCountOfTheLetter |
+--------+----------+---------------------+
| a      |        1 |                   1 |
| b      |        2 |                   2 |
| c      |        3 |                   3 |
+--------+----------+---------------------+