3

I have the following tables

CREATE TABLE Foos (
    [Id] INT IDENTITY,
    -- Other fields
)

CREATE TABLE Boos (
    [Id] INT IDENTITY,
    [FooId] INT,
    -- Other fields
)

I am trying to execute a very simple query:

SELECT f.Id, COUNT(*)
FROM Foos f
JOIN Boos b on f.Id = b.FooId
GROUP BY b.FooId

Obviously I am getting an error because of the GROUP BY. The error message is

Column 'Foo.Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

When I change the group by to GROUP BY f.Id everything works fine again.

My question is, why would SQL Server throw that error while it already knows for sure that f.Id = b.FooId from the join, that Foo.Id is unique from the IDENTITY, and that the grouping will logically return the same count due to the join on a primary key?

4

6 回答 6

3

SQL Server 不能始终如一地推断此类键关系 - 如果 boos.FooId 可以为空并且您的查询是:

SELECT f.Id, COUNT(*)
FROM Foos f
  LEFT JOIN Boos b on f.Id = b.FooId
GROUP BY b.FooId

您有一组记录,其中 b.fooId 为 NULL 并且 f.Id 在它们之间可能不同。

您还可以在其他可能导致 NULLS 或其他类型的不等价的条件下加入 - 例如,您可以像这样加入:

SELECT f.Id, COUNT(*)
FROM Foos f
  JOIN Boos b on SUBSTRING(f.Id, 2, 4) = SUBSTRING(b.FooId, 2, 4)
GROUP BY b.FooId

所以 - 是的,这可以在一个简单、常见的情况下进行推理,但仅限于这种情况。这将导致不一致,并最终证明让开发人员更加沮丧。

于 2013-09-17T10:15:17.837 回答
1

我相信您期望看到的结果是两列数据,例如:

ID   COUNT(*)
--   --------
 2          7
 3          2
 4         13

SQL 引擎需要在 ID 列中显示一些内容,您必须明确告诉它该分组/显示项是什么。引擎不会推断您想看到的内容。

如果您在 Boos 中没有记录而在 Foos 中进行记录,则这将特别重要。在这种情况下,引擎需要知道在零计数旁边显示哪个 ID。

于 2013-09-17T10:20:23.040 回答
1

SQL Server 根本不考虑这些事实,因此它无法推断出与此特定查询中b.FooId的情况始终相同f.Id

于 2013-09-17T10:11:56.990 回答
1

我想给你以下关于使用 group by 条款的建议

标准 group by 和 having 查询 示例部分中的所有 group by 和 having 查询都遵循 SQL 标准,该标准规定使用 group by、have 和向量聚合函数的查询使用以下准则为每个组生成一行和一个汇总值:

选择列表中的列也必须在 group by 表达式中,或者它们必须是聚合函数的参数。

group by 表达式只能包含选择列表中的列名。但是,仅用作选择列表中聚合函数参数的列不符合条件。

having 表达式中的列必须是单值的——例如聚合的参数——并且它们必须在选择列表或 group by 子句中。具有选择列表聚合和have 子句的查询必须具有group by 子句。如果在没有选择列表聚合的查询中省略 group by,则 where 子句未排除的所有行都被视为单个组。

在非分组查询中,“where exclude rows”的原则似乎很简单。在分组查询中,原理扩展为“where 在 group by 之前排除行,并且从结果显示中排除行”。</p>

SQL 标准允许连接两个或多个表的查询使用 group by 和 have,如果它们也遵守上述准则。在指定联接或其他复杂查询时,请使用 group by 和 have 的标准语法,直到您完全理解 Transact-SQL 扩展对这两个子句的影响。

为帮助您避免扩展问题,Adaptive Server 为 set 命令提供了 fipsflagger 选项,该选项在查询中每次出现 Transact-SQL 扩展时都会发出非致命警告。有关更多信息,请参阅设置。

现在在第一个场景组中按用法是无效的,但在第二种情况下它不是。

谢谢尼拉吉·拉蒂

于 2013-09-17T10:21:52.280 回答
0

SQL Server 以传统方式检查您的查询比确保:

  • b.FooId在 GROUP BY 子句中实际上与f.Id在 SELECT 子句中相同
  • 它们不为空
  • 连接是内部的,而不是外部的
于 2013-09-17T10:25:48.113 回答
0

您仍然需要应用您在原始选择语句中定义的适当分组,Select f.Id...因此您需要按此列进行分组。你没有超越 sql server。

于 2013-09-17T10:12:37.883 回答