aiccs-api/check/跨数据库SQL兼容性检测方案.md

828 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 跨数据库SQL兼容性检测方案
## 一、检测目标
检测系统中所有SQL语句在目标数据库达梦/MySQL/Oracle/DB2上的兼容性识别并修复不兼容的SQL语法确保系统平滑迁移到新数据库环境。
---
## 二、检测范围
### 检测对象
1. **Mapper XML文件**所有MyBatis Mapper XML中的SQL语句
2. **Java代码**:使用注解(@Select/@Insert/@Update/@Delete的SQL
3. **动态SQL**Java代码中拼接的SQL字符串
4. **数据库脚本**DDL/DML脚本文件
### 检测项
1. **SQL语法兼容性**
- MySQL特有语法 → 达梦/Oracle语法
- 分页、函数、操作符等
2. **数据类型兼容性**
- MySQL特有类型 → 标准SQL类型
- 类型映射关系
3. **字符串和日期处理**
- 字符串拼接、日期函数等
---
## 三、MySQL到达梦兼容性对照表
| SQL类型 | MySQL语法 | 达梦语法 | 兼容性说明 | 检测方法 |
|--------|-----------|---------|-----------|---------|
| **分页** | `LIMIT 10, 20` | `LIMIT 20 OFFSET 10``ROWNUM` | 达梦8+支持LIMIT | Grep搜索`LIMIT` |
| **自增主键** | `AUTO_INCREMENT` | `IDENTITY``SEQUENCE` | 需修改为IDENTITY | Grep搜索`AUTO_INCREMENT` |
| **日期函数** | `NOW()` | `SYSDATE` | 需替换为SYSDATE | Grep搜索`NOW()` |
| **字符串拼接** | `CONCAT(a,b)``CONCAT_WS()` | `a \|\| b``CONCAT()` | 达梦支持两种方式 | Grep搜索`CONCAT` |
| **反引号** | `` `table` `` | `"table"` | 需替换为双引号 | Grep搜索反引号 |
| **布尔值** | `TRUE/FALSE` | `1/0` | 需替换为1/0 | Grep搜索`TRUE\|FALSE` |
| **注释符号** | `#注释` | `-- 注释` | `#`不支持,需改为`--` | Grep搜索`^[[:space:]]*#` |
| **当前时间** | `CURRENT_TIMESTAMP` | `SYSDATE` | 建议统一用SYSDATE | Grep搜索`CURRENT_TIMESTAMP` |
| **空字符串** | `''` | `''``NULL` | 大部分兼容 | 人工检查 |
| **数据类型** | `TINYINT/MEDIUMINT` | `TINYINT/INT` | TINYINT需调整为INT | Grep搜索`TINYINT\|MEDIUMINT` |
| **索引提示** | `USE INDEX` | 不支持 | 需删除索引提示 | Grep搜索`USE INDEX` |
| **GROUP BY** | 允许SELECT非聚合字段 | 要求SELECT字段都在GROUP BY中 | 需修改SQL | 人工检查 |
| **IF函数** | `IF(condition, a, b)` | `CASE WHEN` | 需改写为CASE WHEN | Grep搜索`\bIF\(` |
| **日期格式化** | `DATE_FORMAT()` | `TO_CHAR()` | 需改写为TO_CHAR | Grep搜索`DATE_FORMAT` |
| **字符串转数字** | `CAST('123' AS SIGNED)` | `CAST('123' AS INT)` | SIGNED改为INT | Grep搜索`AS SIGNED` |
---
## 四、检测方法
### 方案A命令行快速扫描
```bash
#!/bin/bash
# MySQL到达梦SQL兼容性扫描脚本
echo "开始扫描MySQL特有SQL语法..."
echo ""
# 1. 搜索分页语句LIMIT
echo "=== 1. 分页语句 (LIMIT) ==="
grep -rn "LIMIT\s" src/main/resources/mybatis/
echo ""
# 2. 搜索自增主键AUTO_INCREMENT
echo "=== 2. 自增主键 (AUTO_INCREMENT) ==="
grep -rn "AUTO_INCREMENT" src/
echo ""
# 3. 搜索日期函数NOW
echo "=== 3. 日期函数 (NOW) ==="
grep -rn "NOW()" src/
echo ""
# 4. 搜索反引号MySQL特有
echo "=== 4. 反引号 (MySQL特有) ==="
grep -rn '`' src/main/resources/mybatis/
echo ""
# 5. 搜索布尔值TRUE/FALSE
echo "=== 5. 布尔值 (TRUE/FALSE) ==="
grep -rn "\bTRUE\b\|\bFALSE\b" src/
echo ""
# 6. 搜索注释符号(#
echo "=== 6. 注释符号 (#) ==="
grep -rn "^[[:space:]]*#" src/main/resources/mybatis/
echo ""
# 7. 搜索特定数据类型
echo "=== 7. MySQL特有数据类型 (TINYINT/MEDIUMINT) ==="
grep -rn "TINYINT\|MEDIUMINT" src/
echo ""
# 8. 搜索IF函数
echo "=== 8. IF函数 (需改写为CASE WHEN) ==="
grep -rn "\bIF(" src/
echo ""
# 9. 搜索DATE_FORMAT
echo "=== 9. DATE_FORMAT (需改写为TO_CHAR) ==="
grep -rn "DATE_FORMAT" src/
echo ""
# 10. 搜索CAST AS SIGNED
echo "=== 10. CAST AS SIGNED (需改为AS INT) ==="
grep -rn "AS SIGNED" src/
echo ""
# 11. 搜索USE INDEX
echo "=== 11. USE INDEX (达梦不支持) ==="
grep -rn "USE INDEX\|FORCE INDEX\|IGNORE INDEX" src/
echo ""
# 12. 搜索GROUP_CONCAT
echo "=== 12. GROUP_CONCAT (需改写为LISTAGG) ==="
grep -rn "GROUP_CONCAT" src/
echo ""
echo "扫描完成!"
```
**Windows版本PowerShell**
```powershell
# MySQL到达梦SQL兼容性扫描脚本
Write-Host "开始扫描MySQL特有SQL语法..." -ForegroundColor Green
Write-Host ""
# 定义要扫描的模式
$patterns = @{
"分页语句 (LIMIT)" = "LIMIT\s"
"自增主键 (AUTO_INCREMENT)" = "AUTO_INCREMENT"
"日期函数 (NOW)" = "NOW\(\)"
"反引号" = "`""
"布尔值 (TRUE/FALSE)" = "\bTRUE\b|\bFALSE\b"
"注释符号 (#)" = "^\s*#"
"TINYINT/MEDIUMINT" = "TINYINT|MEDIUMINT"
"IF函数" = "\bIF\("
"DATE_FORMAT" = "DATE_FORMAT"
"CAST AS SIGNED" = "AS SIGNED"
"USE INDEX" = "USE INDEX|FORCE INDEX|IGNORE INDEX"
"GROUP_CONCAT" = "GROUP_CONCAT"
}
# 执行扫描
foreach ($item in $patterns.GetEnumerator()) {
Write-Host "=== $($item.Key) ===" -ForegroundColor Yellow
Select-String -Path "src\**\*.xml", "src\**\*.java" -Pattern $item.Value -Recurse
Write-Host ""
}
Write-Host "扫描完成!" -ForegroundColor Green
```
---
### 方案BPython自动化检测工具
```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MySQL到达梦SQL兼容性检测工具
"""
import re
import json
from pathlib import Path
from typing import Dict, List, Tuple
from dataclasses import dataclass, asdict
from datetime import datetime
@dataclass
class CompatibilityIssue:
"""兼容性问题"""
issue_type: str
severity: str # HIGH, MEDIUM, LOW
file_path: str
line_number: int
mysql_pattern: str
dm_pattern: str
description: str
suggestion: str
class MySQLToDMCompatibilityChecker:
"""MySQL到达梦兼容性检测器"""
# 检测规则配置
RULES = {
'LIMIT': {
'pattern': r'\bLIMIT\s+(\d+)\s*,\s*(\d+)',
'severity': 'HIGH',
'mysql_pattern': 'LIMIT offset, count',
'dm_pattern': 'LIMIT count OFFSET offset',
'description': 'MySQL分页语法不兼容达梦',
'suggestion': '将 LIMIT offset, count 改为 LIMIT count OFFSET offset'
},
'AUTO_INCREMENT': {
'pattern': r'\bAUTO_INCREMENT\b',
'severity': 'HIGH',
'mysql_pattern': 'AUTO_INCREMENT',
'dm_pattern': 'IDENTITY',
'description': '达梦不支持AUTO_INCREMENT',
'suggestion': '将 AUTO_INCREMENT 改为 IDENTITY 或使用序列SEQUENCE'
},
'NOW': {
'pattern': r'\bNOW\(\)',
'severity': 'HIGH',
'mysql_pattern': 'NOW()',
'dm_pattern': 'SYSDATE',
'description': '达梦不支持NOW()函数',
'suggestion': '将 NOW() 改为 SYSDATE'
},
'BACKTICK': {
'pattern': r'`',
'severity': 'HIGH',
'mysql_pattern': '`table_name`',
'dm_pattern': '"table_name"',
'description': 'MySQL反引号语法',
'suggestion': '将反引号改为双引号 "table_name"'
},
'BOOLEAN': {
'pattern': r'\b(TRUE|FALSE)\b',
'severity': 'MEDIUM',
'mysql_pattern': 'TRUE/FALSE',
'dm_pattern': '1/0',
'description': '达梦不支持布尔值关键字',
'suggestion': '将 TRUE 改为 1FALSE 改为 0'
},
'COMMENT_HASH': {
'pattern': r'^\s*#',
'severity': 'LOW',
'mysql_pattern': '# 注释',
'dm_pattern': '-- 注释',
'description': '达梦不支持#注释',
'suggestion': '将 # 注释 改为 -- 注释'
},
'TINYINT': {
'pattern': r'\bTINYINT\b',
'severity': 'MEDIUM',
'mysql_pattern': 'TINYINT',
'dm_pattern': 'INT或SMALLINT',
'description': '达梦不完全支持TINYINT',
'suggestion': '将 TINYINT 改为 SMALLINT 或 INT'
},
'IF_FUNCTION': {
'pattern': r'\bIF\s*\(',
'severity': 'HIGH',
'mysql_pattern': 'IF(condition, a, b)',
'dm_pattern': 'CASE WHEN condition THEN a ELSE b END',
'description': '达梦不支持IF()函数',
'suggestion': '将 IF(condition, a, b) 改为 CASE WHEN condition THEN a ELSE b END'
},
'DATE_FORMAT': {
'pattern': r'\bDATE_FORMAT\s*\(',
'severity': 'HIGH',
'mysql_pattern': 'DATE_FORMAT(date, format)',
'dm_pattern': 'TO_CHAR(date, format)',
'description': '达梦不支持DATE_FORMAT函数',
'suggestion': '将 DATE_FORMAT(date, format) 改为 TO_CHAR(date, format)'
},
'CAST_SIGNED': {
'pattern': r'\bCAST\s*\([^)]+\s+AS\s+SIGNED\b',
'severity': 'MEDIUM',
'mysql_pattern': 'CAST(expr AS SIGNED)',
'dm_pattern': 'CAST(expr AS INT)',
'description': '达梦不支持SIGNED类型',
'suggestion': '将 CAST(expr AS SIGNED) 改为 CAST(expr AS INT)'
},
'USE_INDEX': {
'pattern': r'\b(USE|FORCE|IGNORE)\s+INDEX\b',
'severity': 'MEDIUM',
'mysql_pattern': 'USE INDEX (index_name)',
'dm_pattern': '(删除索引提示)',
'description': '达梦不支持索引提示',
'suggestion': '删除 USE INDEX/FORCE INDEX/IGNORE INDEX'
},
'GROUP_CONCAT': {
'pattern': r'\bGROUP_CONCAT\s*\(',
'severity': 'HIGH',
'mysql_pattern': 'GROUP_CONCAT(expr)',
'dm_pattern': 'LISTAGG(expr, delimiter)',
'description': '达梦不支持GROUP_CONCAT函数',
'suggestion': '将 GROUP_CONCAT(expr) 改为 LISTAGG(expr, ",")'
}
}
def __init__(self, source_dir: str):
"""
初始化检测器
:param source_dir: 源代码目录
"""
self.source_dir = Path(source_dir)
self.issues: List[CompatibilityIssue] = []
def check_file(self, file_path: Path) -> List[CompatibilityIssue]:
"""检测单个文件"""
issues = []
content = file_path.read_text(encoding='utf-8')
lines = content.split('\n')
for line_no, line in enumerate(lines, 1):
for rule_name, rule in self.RULES.items():
matches = re.finditer(rule['pattern'], line, re.IGNORECASE)
for match in matches:
issues.append(CompatibilityIssue(
issue_type=rule_name,
severity=rule['severity'],
file_path=str(file_path.relative_to(self.source_dir)),
line_number=line_no,
mysql_pattern=rule['mysql_pattern'],
dm_pattern=rule['dm_pattern'],
description=rule['description'],
suggestion=rule['suggestion']
))
return issues
def check_all(self) -> List[CompatibilityIssue]:
"""检测所有文件"""
# 检测XML文件
xml_files = list(self.source_dir.rglob('**/*.xml'))
# 检测Java文件
java_files = list(self.source_dir.rglob('**/*.java'))
all_files = xml_files + java_files
print(f'开始检测 {len(all_files)} 个文件...\n')
for idx, file_path in enumerate(all_files, 1):
print(f'[{idx}/{len(all_files)}] 检测: {file_path.name}')
issues = self.check_file(file_path)
self.issues.extend(issues)
return self.issues
def generate_report(self, output_file: str = 'compatibility_report.json'):
"""生成检测报告"""
report = {
'scan_time': datetime.now().isoformat(),
'summary': {
'total_issues': len(self.issues),
'by_severity': self._count_by_severity(),
'by_type': self._count_by_type(),
'by_file': self._count_by_file()
},
'issues': [asdict(issue) for issue in self.issues]
}
# 保存JSON报告
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
# 生成Markdown报告
md_file = output_file.replace('.json', '.md')
self._generate_markdown_report(report, md_file)
print(f'\n检测完成!')
print(f'JSON报告: {output_file}')
print(f'Markdown报告: {md_file}')
print(f'\n总计发现 {report["summary"]["total_issues"]} 个兼容性问题:')
for severity, count in report['summary']['by_severity'].items():
print(f' - {severity}: {count}')
def _count_by_severity(self) -> Dict[str, int]:
"""按严重程度统计"""
count = {'HIGH': 0, 'MEDIUM': 0, 'LOW': 0}
for issue in self.issues:
count[issue.severity] += 1
return count
def _count_by_type(self) -> Dict[str, int]:
"""按问题类型统计"""
count = {}
for issue in self.issues:
if issue.issue_type not in count:
count[issue.issue_type] = 0
count[issue.issue_type] += 1
return dict(sorted(count.items(), key=lambda x: x[1], reverse=True))
def _count_by_file(self) -> Dict[str, int]:
"""按文件统计"""
count = {}
for issue in self.issues:
if issue.file_path not in count:
count[issue.file_path] = 0
count[issue.file_path] += 1
return dict(sorted(count.items(), key=lambda x: x[1], reverse=True)[:10])
def _generate_markdown_report(self, report: dict, output_file: str):
"""生成Markdown格式报告"""
with open(output_file, 'w', encoding='utf-8') as f:
f.write('# MySQL到达梦SQL兼容性检测报告\n\n')
f.write(f'**扫描时间**: {report["scan_time"]}\n\n')
# 摘要
f.write('## 检测摘要\n\n')
f.write(f'- **总问题数**: {report["summary"]["total_issues"]}\n')
f.write('\n### 按严重程度\n\n')
f.write('| 严重程度 | 数量 |\n')
f.write('|---------|------|\n')
for severity, count in report['summary']['by_severity'].items():
f.write(f'| {severity} | {count} |\n')
f.write('\n### 按问题类型\n\n')
f.write('| 问题类型 | 数量 |\n')
f.write('|---------|------|\n')
for issue_type, count in list(report['summary']['by_type'].items())[:10]:
f.write(f'| {issue_type} | {count} |\n')
f.write('\n### 问题最多的文件 (Top 10)\n\n')
f.write('| 文件 | 问题数 |\n')
f.write('|------|--------|\n')
for file_path, count in report['summary']['by_file'].items():
f.write(f'| {file_path} | {count} |\n')
# 详细问题列表
f.write('\n## 详细问题清单\n\n')
for issue in report['issues']:
f.write(f'### {issue["issue_type"]} - {issue["severity"]}\n\n')
f.write(f'**文件**: `{issue["file_path"]}:{issue["line_number"]}`\n\n')
f.write(f'**问题描述**: {issue["description"]}\n\n')
f.write(f'- **MySQL语法**: `{issue["mysql_pattern"]}`\n')
f.write(f'- **达梦语法**: `{issue["dm_pattern"]}`\n')
f.write(f'- **修复建议**: {issue["suggestion"]}\n\n')
f.write('---\n\n')
def generate_fix_script(self, output_file: str = 'fix_compatibility.sh'):
"""生成自动修复脚本(仅适用于简单替换)"""
with open(output_file, 'w', encoding='utf-8') as f:
f.write('#!/bin/bash\n')
f.write('# MySQL到达梦SQL兼容性自动修复脚本\n')
f.write('# 注意:此脚本仅处理简单替换,复杂情况需人工确认\n\n')
# 可自动修复的规则
fixable_rules = {
'NOW': ('NOW()', 'SYSDATE'),
'AUTO_INCREMENT': ('AUTO_INCREMENT', 'IDENTITY'),
'BOOLEAN_TRUE': ('\\bTRUE\\b', '1'),
'BOOLEAN_FALSE': ('\\bFALSE\\b', '0'),
}
for rule_name, (old, new) in fixable_rules.items():
f.write(f'# 修复 {rule_name}\n')
f.write(f'find src -type f -name "*.xml" -o -name "*.java" | \\\n')
f.write(f' xargs sed -i "s/{old}/{new}/g"\n\n')
f.write('echo "自动修复完成!请仔细检查修改内容。"\n')
print(f'自动修复脚本已生成: {output_file}')
print('⚠️ 注意:使用前请先提交代码,修复脚本可能产生副作用')
def main():
"""主函数"""
import sys
# 配置
SOURCE_DIR = 'src'
JSON_REPORT = 'check/reports/compatibility_report.json'
FIX_SCRIPT = 'check/scripts/fix_compatibility.sh'
# 创建检测器
checker = MySQLToDMCompatibilityChecker(SOURCE_DIR)
# 执行检测
checker.check_all()
# 生成报告
checker.generate_report(JSON_REPORT)
# 生成修复脚本
checker.generate_fix_script(FIX_SCRIPT)
if __name__ == '__main__':
main()
```
---
## 五、常见SQL兼容性问题及修复示例
### 1. 分页查询
**MySQL写法**
```xml
<select id="selectPage" resultType="...">
SELECT * FROM abnormal_list
LIMIT #{offset}, #{pageSize}
</select>
```
**达梦写法**
```xml
<select id="selectPage" resultType="...">
SELECT * FROM abnormal_list
LIMIT #{pageSize} OFFSET #{offset}
</select>
```
**或者使用ROWNUM**达梦7及以下
```xml
<select id="selectPage" resultType="...">
SELECT * FROM (
SELECT t.*, ROWNUM AS rn FROM abnormal_list t WHERE ROWNUM <= #{offset} + #{pageSize}
) WHERE rn > #{offset}
</select>
```
---
### 2. 日期函数
**MySQL写法**
```xml
<select id="selectByCreateTime" resultType="...">
SELECT * FROM abnormal_list
WHERE create_time &lt;= NOW()
</select>
```
**达梦写法**
```xml
<select id="selectByCreateTime" resultType="...">
SELECT * FROM abnormal_list
WHERE create_time &lt;= SYSDATE
</select>
```
---
### 3. IF函数改为CASE WHEN
**MySQL写法**
```xml
<select id="selectWithStatus" resultType="...">
SELECT
entname,
IF(abnormal_date IS NULL, '正常', '异常') AS status
FROM abnormal_list
</select>
```
**达梦写法**
```xml
<select id="selectWithStatus" resultType="...">
SELECT
entname,
CASE WHEN abnormal_date IS NULL THEN '正常' ELSE '异常' END AS status
FROM abnormal_list
</select>
```
---
### 4. GROUP_CONCAT改为LISTAGG
**MySQL写法**
```xml
<select id="selectGroupConcat" resultType="...">
SELECT
dept_id,
GROUP_CONCAT(entname SEPARATOR ',') AS ent_names
FROM abnormal_list
GROUP BY dept_id
</select>
```
**达梦写法**
```xml
<select id="selectGroupConcat" resultType="...">
SELECT
dept_id,
LISTAGG(entname, ',') WITHIN GROUP (ORDER BY entname) AS ent_names
FROM abnormal_list
GROUP BY dept_id
</select>
```
---
### 5. 字符串拼接
**MySQL写法**
```xml
<select id="selectFullName" resultType="...">
SELECT
CONCAT(last_name, first_name) AS full_name
FROM users
</select>
```
**达梦写法**(两种都支持):
```xml
<select id="selectFullName" resultType="...">
SELECT
last_name || first_name AS full_name
FROM users
</select>
```
```xml
<select id="selectFullName" resultType="...">
SELECT
CONCAT(last_name, first_name) AS full_name
FROM users
</select>
```
---
### 6. CAST类型转换
**MySQL写法**
```xml
<select id="selectCast" resultType="...">
SELECT CAST(amount AS SIGNED) AS int_amount
FROM payments
</select>
```
**达梦写法**
```xml
<select id="selectCast" resultType="...">
SELECT CAST(amount AS INT) AS int_amount
FROM payments
</select>
```
---
### 7. DATE_FORMAT改为TO_CHAR
**MySQL写法**
```xml
<select id="selectFormattedDate" resultType="...">
SELECT
DATE_FORMAT(create_time, '%Y-%m-%d') AS formatted_date
FROM abnormal_list
</select>
```
**达梦写法**
```xml
<select id="selectFormattedDate" resultType="...">
SELECT
TO_CHAR(create_time, 'YYYY-MM-DD') AS formatted_date
FROM abnormal_list
</select>
```
---
## 六、检测步骤
### 1. 准备阶段
```bash
# 创建报告目录
mkdir -p check/reports
mkdir -p check/scripts
# 统计需要检测的文件数量
find src -name "*.xml" | wc -l
find src -name "*.java" | wc -l
```
### 2. 执行检测
**方式1使用命令行快速扫描**
```bash
# Linux/Mac
bash check/scripts/scan_mysql_syntax.sh > scan_results.txt
# Windows PowerShell
powershell -ExecutionPolicy Bypass -File check/scripts/scan_mysql_syntax.ps1 > scan_results.txt
```
**方式2使用Python自动化检测**
```bash
# 运行检测脚本
python check/scripts/mysql_to_dm_checker.py
# 查看报告
cat check/reports/compatibility_report.json
cat check/reports/compatibility_report.md
```
### 3. 分析报告
根据报告中的问题优先级处理:
1. **HIGH高危**
- 影响SQL执行的关键语法
- 必须修复才能正常运行
2. **MEDIUM中危**
- 可能有兼容性问题
- 建议修复
3. **LOW低危**
- 次要兼容性问题
- 可选修复
### 4. 修复问题
**自动修复**(谨慎使用):
```bash
# 先提交代码
git add .
git commit -m "备份:修复前的代码"
# 执行自动修复脚本
bash check/scripts/fix_compatibility.sh
# 检查修复结果
git diff
```
**手动修复**
根据报告中的文件路径和行号,逐一修复兼容性问题。
### 5. 验证修复
```bash
# 重新扫描确认问题已修复
python check/scripts/mysql_to_dm_checker.py
# 对比修复前后的报告
diff check/reports/before_fix.json check/reports/after_fix.json
```
---
## 七、输出报告
### JSON格式报告兼容性检测机器报告
```json
{
"scan_time": "2025-01-05T14:30:00",
"summary": {
"total_issues": 45,
"by_severity": {
"HIGH": 23,
"MEDIUM": 15,
"LOW": 7
},
"by_type": {
"NOW": 18,
"LIMIT": 12,
"IF_FUNCTION": 8,
"AUTO_INCREMENT": 5,
"BOOLEAN": 2
},
"by_file": {
"src/main/resources/mybatis/mapper/aiccs/abnormal/AbnormalListMapper.xml": 8,
"src/main/java/com/chinaweal/aiccs/aiccs/seriousillegal/mapper/SeriousIllegalMapper.java": 5
}
},
"issues": [
{
"issue_type": "NOW",
"severity": "HIGH",
"file_path": "src/main/resources/mybatis/mapper/aiccs/abnormal/AbnormalListMapper.xml",
"line_number": 45,
"mysql_pattern": "NOW()",
"dm_pattern": "SYSDATE",
"description": "达梦不支持NOW()函数",
"suggestion": "将 NOW() 改为 SYSDATE"
}
]
}
```
### Markdown格式报告兼容性检测人工报告
参见工具生成的Markdown格式报告。
---
## 八、注意事项
1. **数据库版本差异**
- 达梦8与达梦7语法有差异
- 确认目标数据库版本
2. **索引提示**
- 达梦不支持MySQL的索引提示语法
- 删除后可能影响查询性能
3. **GROUP BY严格模式**
- 达梦要求SELECT中的非聚合字段必须在GROUP BY中
- MySQL的宽松模式在达梦中会报错
4. **字符串拼接**
- 达梦支持 `||``CONCAT()` 两种方式
- 建议统一使用 `CONCAT()` 以提高代码可读性
5. **NULL处理**
- 达梦的NULL处理与MySQL可能有细微差异
- 注意空字符串 `''``NULL` 的区别
6. **自动生成的SQL**
- MyBatis-Plus等框架生成的SQL也需要检测
- 可能需要配置框架的数据库方言
---
## 九、相关文件
- Mapper XML路径: `src/main/resources/mybatis/mapper/{数据源}/**/*.xml`
- 扫描脚本: `check/scripts/scan_mysql_syntax.sh` / `scan_mysql_syntax.ps1`
- 检测工具: `check/scripts/mysql_to_dm_checker.py`
- 检测报告: `check/reports/compatibility_report.json` / `compatibility_report.md`
- 修复脚本: `check/scripts/fix_compatibility.sh`