为什么这个带有子查询的SQL查询要求限制为单个值的列使用GROUP BY?

为什么这个带有子查询的SQL查询要求限制为单个值的列使用GROUP BY?,sql,sql-server,Sql,Sql Server,为什么下面的SQL查询失败 SELECT a.time, SUM(a.x)*(SELECT SUM(b.weight) FROM b WHERE b.id = a.id AND b.time = a.time) AS z FROM a WHERE a.id = 'FOO' GROUP BY a.time 对于SQL Server 2016,我遇到了一个错误 Msg 8120,第16级,状态1,第9行 列“a.id”在选择列表中无效,因为它未包含在聚合

为什么下面的SQL查询失败

SELECT
    a.time,
    SUM(a.x)*(SELECT SUM(b.weight) FROM b WHERE b.id = a.id AND b.time = a.time) AS z
FROM 
    a
WHERE
    a.id = 'FOO'
GROUP BY 
    a.time
对于SQL Server 2016,我遇到了一个错误

Msg 8120,第16级,状态1,第9行
列“a.id”在选择列表中无效,因为它未包含在聚合函数或GROUP BY子句中

该错误消息意味着SQL Server认为内部查询可以为多个
a.id
生成结果,因此它不知道生成哪一个。我不明白它为什么会这样想,因为外部查询的
WHERE
a.id
限制为单个值

我可以通过将
a.id
添加到外部查询的
groupby
,或者在内部查询中将
a.id
替换为
'FOO'
来消除错误。在我的应用程序中,这两种查询都是不可取的,其中许多查询都是从预制件动态创建的,内部查询最好是固定的(即,不动态插入literal
'FOO'
),外部查询最好不知道内部查询的细节(即,不能仅仅因为内部查询使用了
a.id
而将
a.id
添加到外部查询的
groupby

在不让内部查询和外部查询更加了解彼此的细节的情况下,是否可以解决此问题


编辑:
time
id
字段是这两个表的主键的一部分。

我很想看看是否有人有更好的解决方案,但我过去也做过类似的事情来解决类似的问题

SELECT
 a.time,
 SUM(a.x)*(SELECT SUM(b.weight) FROM b WHERE b.id = MIN(a.id) AND b.time = a.time) AS z
FROM a
WHERE
  a.id='FOO'
GROUP BY a.time

由于您可以确信a.id只能是1个值,因此min将是该值。

我很想知道是否有人有更好的解决方案,但我过去曾做过类似的事情来解决类似的问题

SELECT
 a.time,
 SUM(a.x)*(SELECT SUM(b.weight) FROM b WHERE b.id = MIN(a.id) AND b.time = a.time) AS z
FROM a
WHERE
  a.id='FOO'
GROUP BY a.time
由于您可以确信a.id只能是1个值,因此最小值将是该值。

尝试两个步骤:

;with cte as
(
SELECT
  a.time,
  SUM(a.x) AS z
FROM a
WHERE
  a.id='FOO'
GROUP BY a.time
)

select a.time
    ,a.z*(SELECT SUM(b.weight) FROM b WHERE b.id = a.id AND b.time = a.time) as z
from cte a
尝试两个步骤:

;with cte as
(
SELECT
  a.time,
  SUM(a.x) AS z
FROM a
WHERE
  a.id='FOO'
GROUP BY a.time
)

select a.time
    ,a.z*(SELECT SUM(b.weight) FROM b WHERE b.id = a.id AND b.time = a.time) as z
from cte a

你似乎已经知道原因了。你解释得很好。如果不知道应用程序的限制,很难回答。你能从多大程度上更改查询?a.id='FOO'只保证返回一条记录,如果该字段上有唯一的索引。但我不确定在标准SQL规则下,编译器是否有义务ed检查where子句中的某个字段是否有这样一个唯一的约束,以便删除将其包含在GROUP BY中的要求。我怀疑没有!为什么MSSQL认为内部查询可以为多个
a.id
生成结果,即使外部查询将
a.id
限制为单个值?我认为is将是一个非常狭隘的优化,需要投入时间,对外部查询进行基数分析,作为内部查询编译的一部分,以允许免于遵守一般规则。@Jonathan Willcock:所以我的“为什么”问题的答案是:“因为SQL编译器还没有足够的智能来实现这一点”。很遗憾,但很清楚。您似乎已经知道了原因。您对此进行了完美的解释。在不了解应用程序的限制的情况下,很难回答。您可以对查询进行多大程度的更改?a.id='FOO'只保证返回一条记录,如果该字段上有唯一的索引。我不确定在标准SQL ru下les,编译器有义务检查where子句中的字段是否有这样一个唯一的约束,以便删除将其包含在GROUP BY中的要求。我怀疑没有!为什么MSSQL认为内部查询可以生成多个
a.id
的结果,即使外部查询将
a.id
限制为一个单一的值?我认为这将是一个非常狭窄的优化,需要投入时间,对外部查询进行基数分析,作为内部查询编译的一部分,以免除一般规则的约束。@Jonathan Willcock:所以我的“为什么”问题的答案是:“因为SQL编译器还不够聪明。”很遗憾,但很清楚。我也想到了这个解决方案,但担心
MIN(a.id)
可能在
a.id='Foo'
限制之前得到评估。数据库中有多个
a.id
值。查询的选择部分在所有其他部分之后运行,因此在取最小值之前会过滤a.id。我也想到了这个解决方案,但担心
min(a.id)
可能在
a.id='Foo'
限制之前进行计算。数据库中有多个
a.id
值。查询的选择部分在所有其他部分之后运行,因此在获取最小值之前将过滤a.id。