我有以下表格
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.FooId
,Foo.Id
与IDENTITY
是唯一的,并且分组由于主键上的连接,逻辑上会返回相同的计数吗?
答案 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以传统方式检查查询要比确保:
更容易,更快捷b.FooId
与SELECT子句f.Id
实际上相同