【通知】SQL模板化算法优化

2024年09月01日起,DAS对SQL模板化算法进行逐步优化。

变更背景

慢SQL链路和SQL审计链路的数据分析中,都依赖对每条SQL进行SQL模板化处理,进而针对相同的模板进行聚合分析。当前这个操作存在一些问题,不能很好处理被截断的SQL(由于被截断的位置不确定,有时会导致模板数膨胀,不方便用户聚类查看)。

变更时间

2024年09月01日起,会逐步对两类链路进行升级。

说明

2024年09月01日更新后,SQL模板化算法的迭代优化,不再另行告知,具体请以接口返回为准。

变更范围

涉及的数据库引擎包括:RDS MySQL、RDS PostgreSQL、RDS SQLServer、RDS MariaDB和PolarDB MySQL版。

变更内容

主要优化点:

  • 支持PostgreSQL 中$$ 作为字符串常量定界符(delimiter)的模板化,并修复标识符被错误替换成问号的BUG。

    原始 SQL:

    UPDATE "study" SET "name" = 'xiaoming', "ext" = $${"math":90,"english":91}$$ where id=128;

    优化前:

    UPDATE ? SET ? = ?, ? = $${?:?,?:?}$$ where id=?;

    优化后:

    UPDATE "study" SET "name"=?,"ext"=? WHERE id=?;
  • 对表名和列名的数字后缀进行替换,减少模板数量。

    原始 SQL:

    select * from [school_3].[class].[student_25];

    优化前:

    select * from [school_3].[class].[student_25];

    优化后:

    SELECT * FROM [school_?].[class].[student_?];
  • 针对 SQLServer,去掉前缀,正确解析 sql_type。

    原始 SQL:

    (@P0 nvarchar(4000))select id, name from student WHERE name = @P0;

    优化前:

    模板: (@P0 nvarchar(?))select id, name from student WHERE name = @P0;
    解析后的sql_type: p0

    优化后:

    模板: SELECT id,name FROM student WHERE name=?;
    解析后的sql_type: select
  • 保证语法正确的前提下,尽量减少空格,同时保留原始 SQL 中关键字的大小写(不进行大写转换)。

    原始 SQL:

    select `name` from `student` 
      where `id` = 1 and (`name` = 'xiaoming' or `class` = 2);

    优化前:

    SELECT `name` FROM `student` WHERE `id` = ? AND (`name` = ? OR `class` = ?)

    优化后:

    SELECT `name` FROM `student` WHERE `id`=? AND (`name`=? OR `class`=?);
  • 完整保留原始 SQL 中的括号。

    原始 SQL:

    select `name` from `student` where `id` = 1 and (`name` = 'xiaoming');

    优化前:

    SELECT `name` FROM `student` WHERE `id` = ? AND `name` = ?

    优化后:

    SELECT `name` FROM `student` WHERE `id`=? AND (`name`=?);
  • 不再将case表达式两边的括号转换为"AS"。

    原始 SQL:

    select `name`, ( CASE WHEN score > 90 THEN 'A' END ) `grade` from `student`;

    优化前:

    SELECT `name` , CASE  WHEN score > ? THEN ? END AS `grade` FROM `student`

    优化后:

    SELECT `name`,(CASE WHEN score>? THEN ? END)`grade` FROM `student`;
  • 正确解析"#"之后的内容。

    原始 SQL:

    select `name`, `#grade` from `student`;

    优化前:

    SELECT `name`, `

    优化后:

    SELECT `name`,`#grade` FROM `student`;
  • 处理截断 SQL 时,丢弃不完整括号内所有内容,减少模板数量。

    原始 SQL:

    select `name`, `grade` from `student` where id = (select uid from 

    优化前:

    select `name`, `grade` from `student` where id = (select uid from

    优化后:

    SELECT `name`,`grade` FROM `student` WHERE id=
  • 相似表达式进行合并,避免由于相似表达式个数不同导致的模板膨胀。

    原始 SQL:

    SELECT 
        CASE 
            WHEN score >= 90 THEN 'A'
            WHEN score >= 80 THEN 'B'
            WHEN score >= 70 THEN 'C'
            WHEN score >= 60 THEN 'D'
            ELSE 'F'
        END AS grade
    FROM 
        students;

    优化前:

    SELECT CASE  WHEN score >= ? THEN ? WHEN score >= ? THEN ? WHEN score >= ? THEN ? WHEN score >= ? THEN ? ELSE ? END AS grade FROM students

    优化后:

    SELECT CASE WHEN score>=? THEN ? ELSE ? END AS grade FROM students;

变更影响

  • 调用慢SQL的OpenAPI接口DescribeSlowLogsDescribeSlowLogRecords时,SQLText(SQL语句)对应的SQLHash(SQL模板标识符)的值会变化。

  • 调用SQL洞察的OpenAPI接口GetFullRequestStatResultByInstanceIdGetAsyncErrorRequestListByCode时,SqlId(SQL模板标识符)的值会变化。