一、什么是兼容级别?

SQL Server的兼容级别就像数据库的"时光胶囊",它决定了当前数据库行为应该模仿哪个历史版本。比如将兼容级别设置为140时,数据库会像SQL Server 2017那样处理查询和运行脚本。这个设定通过以下语句就能查看到:

-- 查看当前数据库兼容级别(技术栈:SQL Server 2019)
SELECT compatibility_level 
FROM sys.databases 
WHERE name = 'YourDatabaseName';

数据库版本与兼容级别映射关系:

  • 160 → SQL Server 2022
  • 150 → SQL Server 2019
  • 140 → SQL Server 2017
  • 130 → SQL Server 2016

二、语法差异的实战示例

示例1:STRING_AGG函数的使用(需要兼容级别130+)

-- 创建测试数据(兼容级别≥130时正常运行)
CREATE TABLE EmployeeSkills (
    EmployeeID INT,
    SkillName VARCHAR(50)
);

INSERT INTO EmployeeSkills VALUES 
(1, 'SQL'),(1, 'C#'),(2, 'Java'),(2, 'Python');

-- 新语法示例(需要兼容级别≥130)
SELECT 
    EmployeeID,
    STRING_AGG(SkillName, ', ') AS Skills -- 合并技能列
FROM EmployeeSkills
GROUP BY EmployeeID;
/* 旧版本会报错:
Msg 195, Level 15, State 10: 'STRING_AGG' 不是可识别的内置函数名称 */

示例2:旧式外连接语法失效(兼容级别≥90)

-- 在兼容级别≥90的数据库中运行会报错
SELECT *
FROM Orders, Customers
WHERE Orders.CustomerID *= Customers.CustomerID; -- 旧式外连接符号

/* 错误信息:
Msg 4147, Level 15: 使用旧式外部联接符号的语句必须运行在低于90的兼容级别下 */

三、性能影响的三个关键战场

3.1 查询优化器的选择策略

在兼容级别150下使用新的基数估算器(CE),这个实验展示了查询计划的差异:

-- 使用旧版CE(技术栈:SQL Server 2019)
ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 140;

-- 使用新版CE(默认)
ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 150;

-- 观察同一查询的执行计划差异
SELECT * 
FROM Sales.OrderLines 
WHERE Quantity > 1000;

3.2 参数嗅探的新式处理

当兼容级别≥130时,引入了参数敏感型执行计划优化:

-- 创建测试过程(技术栈:SQL Server 2019)
CREATE PROCEDURE GetOrders @StatusID INT
AS
BEGIN
    SELECT * 
    FROM Sales.Orders 
    WHERE OrderStatusID = @StatusID
    ORDER BY OrderDate DESC;
END

-- 使用兼容级别150时
EXEC GetOrders @StatusID = 5; -- 高频查询值
EXEC GetOrders @StatusID = 1; -- 低频查询值
-- 查询计划缓存中会出现多个优化版本

3.3 批处理模式的演进

在兼容级别≥150时,行存储表也支持批处理模式:

-- 创建测试表(技术栈:SQL Server 2019)
CREATE TABLE WebLogs (
    LogID BIGINT IDENTITY PRIMARY KEY,
    SessionID UNIQUEIDENTIFIER,
    AccessTime DATETIME2,
    URL VARCHAR(500)
);

-- 插入10万条测试数据
INSERT INTO WebLogs 
SELECT NEWID(), GETDATE(), 'https://example.com/page' 
FROM sys.all_columns a CROSS JOIN sys.all_columns b;

-- 比较以下查询在不同兼容级别的执行时间
SELECT 
    COUNT(DISTINCT SessionID),
    AVG(DATEPART(HOUR, AccessTime))
FROM WebLogs;

四、应用场景的决策地图

场景1:数据库升级的过渡期

保持旧的兼容级别,比如将SQL Server 2019的数据库运行在140兼容级别,可以:

  • 规避新版本可能引入的执行计划变更风险
  • 维持现有存储过程的稳定性
  • 为应用程序升级争取时间

场景2:新功能强制使用

当需要使用时间序列函数(如DATEDIFF_BIG)或JSON增强功能时:

-- 需要在兼容级别≥130的数据库中使用(演示JSON增强)
DECLARE @json NVARCHAR(MAX) = N'[
    {"name":"张三","score":85},
    {"name":"李四","score":92}
]';

SELECT *
FROM OPENJSON(@json)
WITH (
    StudentName NVARCHAR(50) '$.name',
    ExamScore INT '$.score',
    ScoreLevel AS CASE 
        WHEN score >= 90 THEN '优秀'
        ELSE '普通' END
);

五、技术选择的权衡艺术

优势比较

选择方向 高级别优势 低级别优势
语法支持 支持所有新特性 兼容旧系统代码
性能优化 新型查询处理器 稳定的执行计划
维护成本 减少技术债务 降低修改成本

典型风险案例

某电商系统升级至兼容级别150后,出现以下问题:

-- 原本正常的查询突然变慢
SELECT *
FROM OrderDetails
WHERE ProductID IN (
    SELECT ProductID 
    FROM DiscontinuedProducts
    WHERE DiscontinuedDate > '2023-01-01'
);

根源分析:

  • 新CE错误估算子查询基数
  • 误用哈希匹配物理运算符
  • 参数嗅探机制失效

解决方案:

-- 使用查询提示强制使用旧CE模型
SELECT *
FROM OrderDetails
WHERE ProductID IN (
    SELECT ProductID 
    FROM DiscontinuedProducts
    WHERE DiscontinuedDate > '2023-01-01'
)
OPTION (USE HINT('FORCE_LEGACY_CARDINALITY_ESTIMATION'));

六、必看的五条军规

  1. 升级前必做功课:使用Query Tuning Assistant分析执行计划变化
  2. 版本生命周期的黄金律:当源版本停止支持后(如SQL 2014),应及时提升兼容级别
  3. 混合场景的处理公式:多数据库系统采用"分级设定策略"
  4. 紧急回退方案:备有ALTER DATABASE语句的快速回滚脚本
  5. 监控三部曲
    • 长期追踪sys.query_store_plan的改进
    • 设置基线性能指标
    • 建立异常执行计划报警机制

七、走出兼容级别的迷雾

经过这趟兼容级别的探险旅程,我们可以看到这个看似简单的设置项实际上是一座连接数据库过去与未来的桥梁。不同的兼容级别选择意味着在"拥抱创新"与"保持稳定"之间的策略博弈。明智的决策应该基于:对新功能的必要性评估、现有系统的复杂程度、以及团队的运维能力三个维度来进行。

那些令人头痛的语法错误,往往源自对新旧特性的认知鸿沟;而那些看似神秘的性能波动,常常是查询优化器在新时代的自我进化。掌握兼容级别的真谛,就是要在变化与稳定之间找到最适合自己业务的平衡点。