sql-server – 兼容级别80的实际行为是什么?

前端之家收集整理的这篇文章主要介绍了sql-server – 兼容级别80的实际行为是什么?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
有人可以让我更好地了解兼容模式功能吗?它的行为与我的预期不同.

据我了解兼容模式,它是关于各种版本的sql Server之间的某些语言结构的可用性和支持.

它不会影响数据库引擎版本的内部工作方式.它会尝试阻止使用早期版本中尚未提供的功能和构造.

我刚刚在sql Server 2008 R2中创建了一个compat等级为80的新数据库.创建一个包含单个int列的表,并用几行填充它.

后执行带有row_number()函数的select语句.

我的想法是,因为row_number函数仅在2005年引入,这将在compat 80模式中引发错误.

但令我惊讶的是,这很好.然后,当然,只有在你“保存”之后才会评估compat规则.所以我为row_number语句创建了一个存储过程.

存储过程创建很顺利,我可以完美地执行它并获得结果.

有人可以帮助我更好地理解兼容模式的工作吗?我的理解显然是有缺陷的.

解决方法

the docs开始:

Sets certain database behaviors to be compatible with the specified version of sql Server.



Compatibility level provides only partial backward compatibility with earlier versions of sql Server. Use compatibility level as an interim migration aid to work around version differences in the behaviors that are controlled by the relevant compatibility-level setting.

在我的解释中,兼容模式是关于语法的行为和解析,而不是像解析器说的那样,“嘿,你不能使用ROW_NUMBER()!”有时,较低的兼容性级别允许您继续使用不再支持的语法,有时它会阻止您使用新的语法结构. The documentation列出了几个明确的例子,但这里有一些演示:

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

代码适用于兼容级别90:

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

但在80年代它产生:

Msg 102,Level 15,State 1
Incorrect Syntax near ‘(‘.

这里的具体问题是在80年代,你不允许将内置函数传递给函数.如果您想保持80兼容模式,可以通过以下方式解决此问题:

DECLARE @db_id INT = DB_ID();

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

将表类型传递给表值函数

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

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,然后再次运行最后三行;您收到此错误消息:

Msg 137,Level 16,State 1,Line 19

Must declare the scalar variable “@foo”.

除了升级compat级别或以不同的方式获得结果之外,没有任何好的解决方法.

在APPLY中使用限定列名称

在90兼容模式及以上,您可以毫无问题地执行此操作:

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

但是,在80兼容模式下,传递给函数的限定列会引发一般语法错误

Msg 102,State 1
Incorrect Syntax near ‘.’.

ORDER BY恰好与列名匹配的别名

考虑这个查询

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

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

001_ofni_epytatad_ps   sp_datatype_info_100
001_scitsitats_ps      sp_statistics_100
001_snmuloc_corps_ps   sp_sproc_columns_100
...

在90兼容模式下,结果完全不同:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

原因?在80兼容模式下,表前缀被完全忽略,因此它按SELECT列表中别名定义的表达式排序.在较新的兼容级别中,会考虑表前缀,因此sql Server实际上会在表中使用该列(如果找到).如果在表中找不到ORDER BY别名,则较新的兼容性级别对于歧义不那么宽容.考虑这个例子:

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

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

Msg 207,Line 3

Invalid column name ‘myname’.

the documentation中也解释了这一点:

When binding the column references in the ORDER BY list to the columns defined in the SELECT list,column ambiguities are ignored and column prefixes are sometimes ignored. This can cause the result set to return in an unexpected order.

For example,an ORDER BY clause with a single two-part column (<table_alias>.<column>) that is used as a reference to a column in a SELECT list is accepted,but the table alias is ignored. Consider the following query.

SELECT c1 = -c1 FROM t_table AS x ORDER BY x.c1

When executed,the column prefix is ignored in the ORDER BY. The sort operation does not occur on the specified source column (x.c1) as expected; instead it occurs on the derived c1 column that is defined in the query. The execution plan for this query shows that the values for the derived column are computed first and then the computed values are sorted.

ORDER BY不在SELECT列表中的东西

在90兼容模式下,您无法执行此操作:

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;

结果:

Msg 104,State 1

ORDER BY items must appear in the select list if the statement contains a UNION,INTERSECT or EXCEPT operator.

但是,在80年代,您仍然可以使用此语法.

旧的,icky外连接

80模式还允许您使用旧的,不推荐使用的外连接语法(* = / = *):

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

sql Server 2008/2008 R2中,如果您处于90或更高版本,则会收到以下详细消息:

Msg 4147,State 1

The query uses non-ANSI outer join operators (“*=” or “=*“). To run this query without modification,please set the compatibility level for current database to 80,using the SET COMPATIBILITY_LEVEL option of ALTER DATABASE. It is strongly recommended to rewrite the query using ANSI outer join operators (LEFT OUTER JOIN,RIGHT OUTER JOIN). In the future versions of sql Server,non-ANSI join operators will not be supported even in backward-compatibility modes.

sql Server 2012中,这根本不再是有效的语法,并产生以下结果:

Msg 102,Line 3

Incorrect Syntax near ‘*=’.

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

没有WITH的表提示

在80 compat模式下,您可以使用以下内容并观察表提示

SELECT * FROM dbo.whatever NOLOCK;

在90年代,NOLOCK不再是表格提示,它是别名.否则,这将工作:

SELECT * FROM dbo.whatever AS w NOLOCK;

但它没有:

Msg 1018,State 1

Incorrect Syntax near ‘NOLOCK’. If this is intended as a part of a table hint,A WITH keyword and parenthesis are now required. See sql Server Books Online for proper Syntax.

现在,为了证明在90 compat模式下第一个示例中没有观察到该行为,请使用AdventureWorks(确保它处于更高的compat级别)并运行以下命令:

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;

这个问题尤其成问题,因为行为在没有错误消息甚至错误的情况下发生变化.它也是升级顾问和其他工具甚至可能都没有发现的东西,因为众所周知,这是一个表别名.

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

sql Server 2008中引入的新日期/时间类型(例如date和datetime2)支持的范围比原始datetime和smalldatetime大得多.无论兼容级别如何,显式转换支持范围之外的值都将失败,例如:

SELECT CONVERT(SMALLDATETIME,'00010101');

产量:

Msg 242,State 3
The conversion of a varchar data type to a smalldatetime data type resulted in an out-of-range value.

但是,隐式转换将在较新的兼容级别中发挥作用.例如,这将在100中工作:

SELECT DATEDIFF(DAY,CONVERT(SMALLDATETIME,SYSDATETIME()),'00010101');

但是在80(以及90)中,它会产生与上面类似的错误

Msg 242,State 3
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.

触发器中的冗余FOR子句

这是an obscure scenario that came up here.在80兼容模式下,这将成功:

CREATE TABLE dbo.x(y INT);
GO
CREATE TRIGGER tx ON dbo.x
FOR UPDATE,UPDATE
------------^^^^^^ notice the redundant UPDATE
AS PRINT 1;

在90兼容性和更高,这不再解析,而是您收到以下错误消息:

Msg 1034,Procedure tx

Syntax error: Duplicate specification of the action “UPDATE” in the trigger declaration.

PIVOT / UNPIVOT

某些形式的语法在80下不起作用(但在90中工作得很好):

SELECT col1,col2
FROM dbo.t1
UNPIVOT (value FOR col3 IN ([x],[y])) AS p;

这会产生:

Msg 156,State 1
Incorrect Syntax near the keyword ‘for’.

对于某些变通方法,包括CROSS APPLY,请参阅these answers.

新的内置功能

尝试在兼容级别<的数据库中使用TRY_CONVERT()等新函数. 110.根本就没有人认识到他们.

SELECT TRY_CONVERT(INT,1);

结果:

Msg 195,State 10

‘TRY_CONVERT’ is not a recognized built-in function name.

建议

如果您确实需要,只能使用80兼容模式.由于它在2008 R2之后的下一个版本中将不再可用,您要做的最后一件事就是在此compat级别编写代码,依赖于您看到的行为,然后在您不能再使用时会出现一大堆破损使用那个compat级别.要有前瞻性思维,不要试图通过花时间继续使用旧的,不推荐使用的语法来将自己描绘成一个角落.

猜你在找的MsSQL相关文章