MS Access / SQL Server Len()Brain Teaser

时间:2011-11-03 16:35:59

标签: sql-server ms-access

我最近在链接到SQL Server后端的表中查找错误数据。我运行了以下查询:

SELECT Len(MyField) AS FieldLength, "#" & MyField & "#" AS FieldContents
FROM MyTable
WHERE Len(MyField) <> 10

我得到了以下结果:

FieldLength    FieldContents
-----------------------------
10             #M1023-324 #
10             #X3253-215 #

因此,在特别告知查询不返回长度为10的字段后,它返回了几条长度为10的字段的记录。所有字段都有尾随空格。

我确实弄清楚发生了什么,这种事情很容易让一个毫无戒心的开发/ DBA(像我一样)绊倒。那么谁知道这里发生了什么?

3 个答案:

答案 0 :(得分:2)

@Andriy M获奖。正如他正确猜到的那样, SQL Server处理WHERE子句,MS Access处理SELECT子句

通常这会被忽视,但 Transact-SQL LEN()会在计算字符前修剪尾随空格,而 VBA Len()函数不会 。请阅读下面的完整说明。


我运行了SQL Server Profiler,看看MS Access实际上是在幕后发送了什么,这就是我得到的:

SELECT "dbo"."MyTable"."MyTableID" 
FROM "dbo"."MyTable" 
WHERE NOT(({fn length("MyField" )}= 10 ) )

上述查询返回了符合条件Len(MyField) <> 10的所有记录的主键。 {fn length()}ODBC Canonical Function

MS SQL Server使用自己的Len() function实现{fn length()}规范函数。正如@Sir Crispalot在他的回答中所指出的,Transact-SQL Len函数修剪了尾随空格。所以在MS SQL Server中Len('M1023-324 ') = 9。当然,在VBA中,Len("M1023-324 ") = 10

如果仔细查看上面的查询,您会发现它实际上并没有返回我在原始SELECT语句中请求的字段。相反,它只返回MS Access的足够信息(即主键字段),以便在需要时请求完整记录。

出于性能原因这样做。如果我的查询返回了30,000行,并且每行中有三十个字段,那么如果我在数据表中一次只显示10行,那么从SQL Server中提取所有信息是没有意义的。所以Access做的是获取所有这些主键,然后一次从SQL Server请求单个记录。此外,它实际上在SQL Server中创建临时存储过程,它执行它以返回这些单独的行。我现在正在偏离主题,但如果您有机会使用SQL Server Profiler,那么值得做一些实验来了解Access实际上在幕后做什么。

无论如何,在SELECT子句中,MS Access仅从SQL Server请求字段本身的内容。也就是说,它请求MyField而不是'#' & MyField & '#'。它还意味着它请求MyField而不是Len(MyField)

最终结果是SQL Server执行过滤操作(WHERE子句)的计算,MS Access执行显示操作的计算(SELECT子句)。并且因为Len()函数在每个环境中的行为略有不同,我们最终会得到一些人认为非常混乱的结果:

10 <> 10

但这对我来说非常有意义。

答案 1 :(得分:0)

我重现了where子句的奇怪行为,所以看了一下文档。

取自MSDN - “返回指定字符串表达式的字符数,不包括尾随空白。”

因此根据SQL,“M1023-324”的长度为9。

仍然没有解释为什么他们在第一列中都有10个。我无法重现那一点!

答案 2 :(得分:0)

看到你的提示之后:是不是因为Sql Server中的字段是char而不是varchar?