子查询列名称中的错误大小会导致错误的结果,但没有错误

时间:2012-06-01 11:43:15

标签: sql-server collation

使用SQL Server Management Studio,我得到了一些不受欢迎的结果(看起来像是一个bug ...?)

如果我使用(而不是字段用于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

我收到无效的列名错误。

我肯定会在第一种情况下得到这个错误吗?

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

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

2 个答案:

答案 0 :(得分:5)

让我们用不依赖于区分大小写的东西来说明正在发生的事情:

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 :(得分:3)

非常有趣的发现。不言而喻的任务是,您总是应该在子查询中对表进行别名,并使用这些别名来明确您的列来自哪个表。子查询允许您引用外部查询中的一个字段,这是导致问题的原因,但在您的方案中,我同意默认值应该是内部查询的字段列表,或者为您提供列歧义错误。无论如何,以下方法总是更可取:

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