【问题标题】:Wrong case in subquery column name causes incorrect results, but no error子查询列名大小写错误导致结果不正确,但没有错误
【发布时间】:2012-06-01 11:43:15
【问题描述】:

使用 SQL Server Management Studio,我得到了一些不想要的结果(对我来说看起来像是一个错误..?)

如果我使用(字段而不是 other_table 的字段):

SELECT * FROM main_table WHERE field IN (SELECT FIELD FROM other_table)

我从 main_table 获得所有结果。

使用正确的大小写:

SELECT * FROM main_table WHERE field IN (SELECT field FROM other_table)

我得到了其他字段出现的预期结果。

自行运行子查询:

SELECT FIELD FROM other_table

我收到一个无效的列名错误。

当然我应该在第一种情况下得到这个错误?

这与排序规则有关吗? 数据库是二进制排序规则。 但是,服务器不区分大小写。 在我看来,服务器组件说“这段代码没问题”并且不允许数据库说该字段是错误的名称..?

我的解决方案有哪些选择?

【问题讨论】:

  • 不,这是区分大小写排序规则的正确行为。 main_table 必须有一个名为 FIELD 的列,因此它使用它,因为它是该范围内该大小写的唯一列名。如果您的数据库具有二进制排序规则,您需要使用正确的大小写来引用表/列名称。最好使用表前缀,这样可以捕捉到这种错误。 SELECT * FROM main_table WHERE field IN (SELECT o.field FROM other_table o)
  • 这是一个示例,说明为什么总是指定哪个表以及哪个字段很有价值。看看如果将所有出现的fieldFIELD 更改为other_table.fieldmain_table.field 等会发生什么。
  • 谢谢大家。我现在已经给表格起了别名并且没有问题。我什至没有想到它正在查看 main_table 中的 FIELD。

标签: sql-server collation


【解决方案1】:

让我们用不区分大小写的东西来说明发生了什么:

USE tempdb;
GO

CREATE TABLE dbo.main_table(column1 INT);

CREATE TABLE dbo.other_table(column2 INT);

INSERT dbo.main_table SELECT 1 UNION ALL SELECT 2;
INSERT dbo.other_table SELECT 1 UNION ALL SELECT 3;

SELECT column1 FROM dbo.main_table
WHERE column1 IN (SELECT column1 FROM dbo.other_table);

结果:

column1
-------
1
2

为什么不会引发错误? SQL Server 正在查看您的查询,发现里面的 column1 不可能在 other_table 中,因此它正在推断和“使用”外部引用表中存在的 column1(就像您可以引用仅存在于没有表引用的外部表)。想想这种变化:

SELECT [column1] FROM dbo.main_table
WHERE EXISTS (SELECT [column1] FROM dbo.other_table WHERE [column2] = [column1]);

结果:

column1
-------
1

SQL Server 再次知道 where 子句中的 column1 在本地引用的表中也不存在,但它会尝试在外部范围内找到它。因此,在想象的世界中,您可能会认为查询实际上是在说:

SELECT m.[column1] FROM dbo.main_table AS m
WHERE EXISTS (SELECT m.[column1] FROM dbo.other_table AS o WHERE o.[column2] = m.[column1]);

(我不是这样输入的,但如果我这样输入,它仍然有效。)

在某些情况下它没有逻辑意义,但查询引擎就是这样做的,并且必须一致地应用规则。在你的情况下(没有双关语),你有一个额外的复杂性:区分大小写。 SQL Server 在您的子查询中没有找到FIELD,但它确实在外部查询中找到了它。所以有几节课:

  1. 始终在列引用前加上表名或别名(以及always prefix your table references with the schema)。
  2. 始终创建和引用您的表、列和其他实体using consistent case尤其是在使用二进制或区分大小写的排序规则时。

【讨论】:

  • 很好的答案和很好的解释,谢谢。第 1 课适合我。第 2 课有点棘手,因为我要处理来自各种来源的许多巨大数据集,有些我可以控制,有些我不能控制(这是一个外部数据集,我最初的问题来自有问题的领域,总是被大写其他数据集,但在这个小写)稍后我会阅读您的博客,再次感谢!克里斯
  • 如果您使用二进制或区分大小写的排序规则,您最好养成正确输入大小写的习惯...有时由于延迟名称解析,这些错误不会在编译时被捕获,而且等到后来才被抓到的时候就很尴尬了。
  • 我工作的环境中,我们的关键数据集都是区分大小写的(嗯,二进制)排序规则——我更喜欢这样!在这种情况下,我使用的是其他人的数据集,他将我们的主要链接标识符(错误地)命名为小写,并且根本没有检查数据集的列名,没有收到错误并假设一切正常。我的尴尬是有限的,幸运的是一位同事发现了一些不正确的地方,我没有意识到我们的 IT 将服务器设置为 CI,但在这种情况下,这无论如何都是一个红鲱鱼。
【解决方案2】:

非常有趣的发现。不言而喻的要求是,您始终应该为子查询中的表设置别名,并使用这些别名来明确说明您的列来自哪个表。子查询允许您从外部查询中引用一个字段,这是您的问题的原因,但在您的场景中,我同意默认值应该是内部查询的字段列表,或者给您一个列歧义错误。无论如何,以下这种方法总是更可取:

select * from main_table a where a.field in (select x.field from other_table x)

【讨论】:

  • 这看起来确实是正确的答案,清晰明了,并且确实解决了我的问题。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 1970-01-01
  • 1970-01-01
  • 2013-06-19
  • 1970-01-01
  • 2016-02-14
  • 2018-01-17
相关资源
最近更新 更多