我是否智取SQL Server或者有这个原因吗?

时间:2013-09-17 10:07:39

标签: sql sql-server select join group-by

我有以下表格

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

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

我正在尝试执行一个非常简单的查询:

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

显然我因GROUP BY而收到错误。错误消息是

  

列'Foo.Id'在选择列表中无效,因为它不是   包含在聚合函数或GROUP BY子句中。

当我将组更改为GROUP BY f.Id时,一切正常。

我的问题是,为什么SQL Server会抛出该错误,因为它已经确定来自联接的f.Id = b.FooIdFoo.IdIDENTITY是唯一的,并且分组由于主键上的连接,逻辑上会返回相同的计数吗?

6 个答案:

答案 0 :(得分:3)

SQL Server无法一致地推断出这样的关键关系 - 如果boost.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

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

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

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

答案 3 :(得分:1)

我想在下面提供有关使用group by子句的建议

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

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

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

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

在非编排查询中,“排除行的位置”的原则似乎很简单。在分组查询中,原则扩展为“在分组前排除行,并从结果显示中排除行。”

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

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

现在在第一个场景中,按使用率组无效,但在第二种情况下则不是。

由于 Niraj Rathi

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

更容易,更快捷
    GROUP BY子句中的
  • b.FooId与SELECT子句
  • 中的f.Id实际上相同
  • 他们不是空的
  • 加入是内在的,而不是外在的