有人可以让我对兼容模式功能有更好的了解吗?它的行为与我预期的有所不同。

据我所知,兼容模式是关于SQL Server各个版本之间某些语言结构的可用性和支持。

它不会影响数据库引擎版本的内部工作。它将尝试阻止使用早期版本中尚不可用的功能和构造。

我刚刚在SQL Server 2008 R2中创建了一个兼容级别为80的新数据库。创建了一个具有单个int列的表,并在其中填充了几行。

然后使用row_number()函数执行了一条select语句。

我的想法是,因为row_number函数才在2005年推出,因此会在compat 80模式下引发错误。然后,可以肯定的是,仅当您“保存某些内容”时才评估兼容规则。因此,我为row_number语句创建了一个存储的proc。

存储的proc创建很好,我可以完美地执行它并获得结果。兼容模式的工作?我的理解显然有缺陷。

#1 楼

来自文档:


将某些数据库行为设置为与指定版本的SQL Server兼容。
...

兼容性级别仅提供与SQL Server早期版本的部分向后兼容性。使用兼容性级别作为临时迁移辅助工具来解决由相关兼容性级别设置控制的行为的版本差异。在我的解释中,兼容性模式涉及行为和解析。语法,而不是解析器说“嘿,您不能使用ROW_NUMBER()!”之类的东西。有时,较低的兼容性级别使您可以继续摆脱不再受支持的语法,有时,它使您无法使用新的语法结构。该文档列出了几个显式示例,但是这里有一些演示:


将内置函数作为函数参数传递

此代码在兼容级别90+下工作:

SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL);


但是在80中它产生:


消息102,级别15,状态1'('附近的语法不正确。


这里的特定问题是在80中不允许将内置函数传递给函数。如果要保持80兼容模式,则可以解决此问题。通过说:

DECLARE @db_id INT = DB_ID();

SELECT * 
FROM sys.dm_db_index_physical_stats(@db_id, NULL, NULL, NULL, NULL);



将表类型传递给表值函数

与上面类似,可以使用TVP并尝试将其传递给表值函数时会出现语法错误,这在现代兼容级别下适用:

CREATE TYPE dbo.foo AS TABLE(bar INT);
GO
CREATE FUNCTION dbo.whatever
(
  @foo dbo.foo READONLY
)
RETURNS TABLE
AS 
  RETURN (SELECT bar FROM @foo);
GO

DECLARE @foo dbo.foo;
INSERT @foo(bar) SELECT 1;
SELECT * FROM dbo.whatever(@foo);


但是,更改兼容性级别到80,然后再次运行最后三行;您会收到以下错误消息:


消息137,级别16,状态1,行19
声明标量变量“ @foo”。


除了升级compat级别或以其他方式获得结果外,没有什么好的解决方法。 >

在APPLY中使用合格的列名

在90兼容模式及更高版本中,您可以毫无问题地进行此操作:模式下,传递给该函数的合格列会引发一般语法错误:


消息102,级别15,状态1'。'附近的语法不正确。 br />
按正好与列名匹配的别名进行排序

请考虑以下查询:

SELECT * FROM sys.dm_exec_cached_plans AS p
  CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t;


与80兼容模式,结果如下:

SELECT name = REVERSE(name), realname = name 
FROM sys.all_objects AS o
ORDER BY o.name;


在90兼容模式下,结果有很大不同: br?原因?在80兼容模式下,表前缀将被完全忽略,因此按SELECT列表中别名定义的表达式进行排序。在较新的兼容性级别中,将考虑使用表前缀,因此SQL Server实际上将使用表中的该列(如果找到)。如果在表中未找到ORDER BY别名,则较新的兼容性级别不会太含糊。请看下面的示例:<43,
001_ofni_epytatad_ps   sp_datatype_info_100
001_scitsitats_ps      sp_statistics_100
001_snmuloc_corps_ps   sp_sproc_columns_100
...


结果由myname表达式在80中排序,因为再次忽略了表前缀,但在90中它会生成以下错误消息: br />

消息207,级别16,状态1,第3行
无效的列名“ myname”。


所有解释为很好地在文档中:将ORDER BY列表中的列引用绑定到SELECT列表中定义的列时,会忽略列歧义,有时会忽略列前缀。这可能会导致结果集以意外的顺序返回。
例如,接受一个ORDER BY子句,该子句具有一个由两部分组成的单列(<table_alias>.<column>),用作对SELECT列表中的列的引用,表别名将被忽略。请考虑以下查询。
SELECT c1 = -c1 FROM t_table AS x ORDER BY x.c1
执行时,在ORDER BY中将忽略列前缀。排序操作不会按预期在指定的源列(x.c1)上发生;相反,它发生在查询中定义的派生c1列上。此查询的执行计划表明,首先计算派生列的值,然后对计算出的值进行排序。 br />
在90兼容模式下,您不能执行以下操作:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...


结果:


消息104,级别16,状态1
如果语句包含UNION,INTERSECT或EXCEPT运算符,则ORDER BY项必须出现在选择列表中。


在80中,您仍可以使用此语法。


旧的,有害的外部联接

80模式还允许您使用旧的,已弃用的外部联接语法(*=/=*): br />
SELECT myname = REVERSE(name), realname = name 
FROM sys.all_objects AS o
ORDER BY o.myname;


在SQL Server 2008/2008 R2中,如果您年满90岁或更高,则会收到以下详细消息:


消息4147,级别15,状态1
查询使用非ANSI外部联接运算符(“ *=”或“ =*”)。要不加修改地运行此查询,请使用ALTER DATABASE的SET COMPATIBILITY_LEVEL选项将当前数据库的兼容性级别设置为80。强烈建议使用ANSI外部联接运算符(LEFT OUTER JOIN,RIGHT OUTER JOIN)重写查询。在SQL Server的未来版本中,即使在向后兼容模式下也将不支持非ANSI连接运算符。


在SQL Server 2012中,此语法不再有效,并产生以下内容:


消息102,级别15,状态1,第3行
'* ='附近的语法不正确。


当然,在SQL Server 2012中,由于不再支持80,因此无法再使用兼容性级别来解决此问题。如果以80兼容模式升级数据库(通过就地升级,分离/附加,备份/还原,日志传送,镜像等),它将自动为您升级到90。


不带WITH
的表提示

在80兼容模式下,您可以使用以下内容,并将观察到表提示: >在90以后,NOLOCK不再是表提示,而是别名。否则,这将起作用:

SELECT name = COALESCE(a.name, '') FROM sys.objects AS a
UNION ALL
SELECT name = COALESCE(a.name, '') FROM sys.objects AS a
ORDER BY a.name;


,但不起作用:


消息1018,级别15,状态1
'NOLOCK'附近的语法不正确。如果要将其用作表提示的一部分,则现在需要A WITH关键字和括号。有关正确的语法,请参见SQL Server联机丛书。兼容级别)并运行以下命令:

SELECT o.name, c.name
FROM sys.objects AS o, sys.columns AS c
WHERE o.[object_id] *= c.[object_id];


这是一个特别有问题的问题,因为行为发生了变化,而没有错误消息甚至没有错误。而且这也是升级顾问和其他工具可能找不到的东西,因为据它所知,那只是表别名。


涉及新日期/时间类型的转换

SQL Server 2008中引入的新日期/时间类型(例如datedatetime2)比原始datetimesmalldatetime支持更大的范围。无论兼容级别如何,都无法明确转换超出支持范围的值,例如:
>消息242,级别16,状态3将varchar数据类型转换为smalldatetime数据类型导致值超出范围。


但是,隐式转换将在较新的兼容性级别中起作用。例如,这将在100+版本中起作用: />
消息242,级别16,状态3将varchar数据类型转换为日期时间数据类型导致值超出范围。



>触发器中的冗余FOR子句

这里出现了一个晦涩的场景。在80兼容模式下,此操作将成功:

SELECT * FROM dbo.whatever NOLOCK; 


在90兼容及更高版本中,此解析不再进行,而是出现以下错误消息:


消息1034,级别15,状态1,过程tx
语法错误:在触发器声明中重复指定了操作“ UPDATE”。



PIVOT / UNPIVOT

某些语法形式在80岁以下无法正常工作(但在90或90岁以上才能正常工作):

SELECT * FROM dbo.whatever AS w NOLOCK;


这会产生:


消息156,级别15,状态1
关键字'for'附近的语法不正确。


有关某些变通办法,包括CROSS APPLY的信息,请参见以下答案。


新的内置函数

在兼容级别的数据库中尝试使用TRY_CONVERT()之类的新功能< 110.根本根本无法识别它们。

BEGIN TRANSACTION;
SELECT TOP (1) * FROM Sales.SalesOrderHeader UPDLOCK;
SELECT * FROM sys.dm_tran_locks 
  WHERE request_session_id = @@SPID
  AND resource_type IN ('KEY', 'OBJECT'); -- how many rows here? 0
COMMIT TRANSACTION;

BEGIN TRANSACTION;
SELECT TOP (1) * FROM Sales.SalesOrderHeader WITH (UPDLOCK);
SELECT * FROM sys.dm_tran_locks
  WHERE request_session_id = @@SPID
  AND resource_type IN ('KEY', 'OBJECT'); -- how many rows here? 2
COMMIT TRANSACTION;


结果:


消息195,级别15,状态10
'TRY_CONVERT'不是公认的内置i n函数名称。



推荐

仅在实际需要时才使用80兼容模式。由于在2008 R2之后的下一版本中将不再提供该功能,因此您要做的最后一件事是在此兼容级别下编写代码,依靠您看到的行为,然后在无法继续运行时遇到很多麻烦使用该兼容级别。善于前瞻性思考,不要试图抽出时间继续使用旧的,过时的语法,将自己陷于困境。

评论


显然,这是比我更好的答案!

– Max Vernon♦
2013年6月20日4:20在

非常感谢您精心设计的答案,亚伦!并修复了我的许多拼写错误。

–复数
2013年6月20日4:47

可在此处找到SQL Server 2014兼容性说明:msdn.microsoft.com/zh-cn/library/bb510680(v=sql.120).aspx

–乔什·加拉格尔(Josh Gallagher)
14-10-21在7:47

#2 楼

兼容性级别仅用于允许从早期版本的SQL Server进行受控迁移。 Compat Level 90并不排除使用新功能的可能性,它仅表示数据库的某些方面以与SQL Server 2005的工作方式兼容的方式得以保留。 com / en-us / library / bb510680.aspx了解更多信息。