# DDL Parse API 格式修复说明

## 问题描述

`/ddl/parse` 接口存在格式不一致的问题：

### 问题1：提示词冲突
在 `DDLParser.parse_ddl()` 方法中存在两个不同的格式要求：
- **System Message** 要求单表格式：`table_info + columns`
- **User Message** 使用 `_optimize_ddl_prompt()` 要求多表格式：`{table_name: {name_zh, meta: [...]}}`

两个提示词相互冲突，导致LLM返回不确定的格式。

### 问题2：缺少关键字段
当前格式缺少参考文档中的重要字段：
- ❌ `is_primary` - 是否主键
- ❌ `comment` - 注释内容
- ❌ `nullable` - 是否可为空

---

## 修复方案

### 1. 统一提示词格式

**修复前**（第86-140行）：
```python
def parse_ddl(self, sql_content):
    prompt = self._optimize_ddl_prompt()
    payload = {
        "model": self.model_name,
        "messages": [
            {
                "role": "system",
                "content": """请从上传的SQL DDL文件中提取数据库表的结构信息...
                {
                  "table_info": {...},
                  "columns": [...]
                }
                """
            },
            {
                "role": "user", 
                "content": f"{prompt}\n\n{sql_content}"
            }
        ]
    }
```

**修复后**：
```python
def parse_ddl(self, sql_content):
    prompt = self._optimize_ddl_prompt()
    payload = {
        "model": self.model_name,
        "messages": [
            {
                "role": "system",
                "content": "你是一个专业的SQL DDL语句解析专家，擅长从DDL语句中提取表结构信息并转换为结构化的JSON格式。"
            },
            {
                "role": "user", 
                "content": f"{prompt}\n\n{sql_content}"
            }
        ]
    }
```

### 2. 更新 `_optimize_ddl_prompt()` 函数

**修复前**（第251-289行）：
```python
def _optimize_ddl_prompt(self):
    return """
    ...
    参考格式如下：
    {
        "users_table": {
            "name_zh": "用户表",
            "schema": "public",
            "meta": [{
                "name_en": "id",
                "data_type": "integer",
                "name_zh": "用户ID"
            }]
        }
    }
    """
```

**修复后**：
```python
def _optimize_ddl_prompt(self):
    return """
    请解析以下DDL建表语句，并按照指定的JSON格式返回结果：
    
    规则说明:
    1. 从DDL语句中识别所有表，可能会有多个表。每个表作为一个独立条目。
    2. 表的英文名称(name_en)使用原始大小写，不要转换为小写。
    3. 表的中文名称(name_zh)提取规则：
       - 优先从COMMENT ON TABLE语句中提取
       - 如果没有注释，则name_zh为空字符串
    4. 对于每个表，提取所有字段信息到columns数组中，每个字段包含：
       - name_zh: 字段中文名称
       - name_en: 字段英文名称（保持原始大小写）
       - data_type: 数据类型（包含长度信息，如VARCHAR(22)）
       - is_primary: 是否主键（"是"或"否"，从PRIMARY KEY约束判断）
       - comment: 注释内容（从COMMENT ON COLUMN提取完整注释）
       - nullable: 是否可为空（"是"或"否"，从NOT NULL约束判断）
    
    返回格式（支持多表）：
    {
        "TB_JC_KSDZB": {
            "table_info": {
                "name_zh": "科室对照表",
                "name_en": "TB_JC_KSDZB"
            },
            "columns": [
                {
                    "name_zh": "医疗机构代码",
                    "name_en": "YLJGDM",
                    "data_type": "VARCHAR(22)",
                    "is_primary": "是",
                    "comment": "医疗机构代码，复合主键",
                    "nullable": "否"
                }
            ]
        }
    }
    """
```

---

## 修复后的格式

### 单表示例（符合参考文档）

```json
[
  {
    "table_info": {
      "name_zh": "科室对照表",
      "name_en": "TB_JC_KSDZB"
    },
    "columns": [
      {
        "name_zh": "医疗机构代码",
        "name_en": "YLJGDM",
        "data_type": "VARCHAR(22)",
        "is_primary": "是",
        "comment": "医疗机构代码，复合主键",
        "nullable": "否"
      },
      {
        "name_zh": "HIS科室代码",
        "name_en": "HISKSDM",
        "data_type": "CHAR(20)",
        "is_primary": "是",
        "comment": "HIS科室代码，主键、唯一",
        "nullable": "否"
      }
    ]
  }
]
```

### 多表示例

```json
[
  {
    "table_info": {
      "name_zh": "科室对照表",
      "name_en": "TB_JC_KSDZB"
    },
    "columns": [...]
  },
  {
    "table_info": {
      "name_zh": "用户表",
      "name_en": "TB_USER"
    },
    "columns": [...]
  }
]
```

### API完整响应格式

```json
{
  "code": 200,
  "data": [
    {
      "table_info": {
        "name_zh": "科室对照表",
        "name_en": "TB_JC_KSDZB"
      },
      "columns": [...]
    }
  ],
  "message": "操作成功"
}
```

---

## 关键改进

### ✅ 1. 统一提示词
- 删除了冲突的 system message
- 只使用 `_optimize_ddl_prompt()` 来定义格式

### ✅ 2. 添加缺失字段
- `is_primary`: 是否主键（"是"/"否"）
- `comment`: 完整的注释内容
- `nullable`: 是否可为空（"是"/"否"）

### ✅ 3. 使用标准结构
- `table_info`: 包含表的基本信息
- `columns`: 字段数组，每个字段包含所有必需属性

### ✅ 4. 保持原始大小写
- 表名和字段名保持原始大小写（如 `TB_JC_KSDZB`, `YLJGDM`）

### ✅ 5. 支持多表
- 使用JSON数组格式返回多个表
- 不使用表名作为key，而是将所有表放在数组中
- 可以一次解析包含多个表的DDL文件

---

## 测试用例

### 输入：科室对照表SQL

```sql
CREATE TABLE TB_JC_KSDZB (
    YLJGDM VARCHAR(22) NOT NULL,
    HISKSDM CHAR(20) NOT NULL,
    HISKSMC CHAR(20) NOT NULL,
    PRIMARY KEY (YLJGDM, HISKSDM)
);

COMMENT ON TABLE TB_JC_KSDZB IS '科室对照表';
COMMENT ON COLUMN TB_JC_KSDZB.YLJGDM IS '医疗机构代码，复合主键';
COMMENT ON COLUMN TB_JC_KSDZB.HISKSDM IS 'HIS科室代码，主键、唯一';
COMMENT ON COLUMN TB_JC_KSDZB.HISKSMC IS 'HIS科室名称';
```

### 预期输出

```json
[
  {
    "table_info": {
      "name_zh": "科室对照表",
      "name_en": "TB_JC_KSDZB"
    },
    "columns": [
      {
        "name_zh": "医疗机构代码",
        "name_en": "YLJGDM",
        "data_type": "VARCHAR(22)",
        "is_primary": "是",
        "comment": "医疗机构代码，复合主键",
        "nullable": "否"
      },
      {
        "name_zh": "HIS科室代码",
        "name_en": "HISKSDM",
        "data_type": "CHAR(20)",
        "is_primary": "是",
        "comment": "HIS科室代码，主键、唯一",
        "nullable": "否"
      },
      {
        "name_zh": "HIS科室名称",
        "name_en": "HISKSMC",
        "data_type": "CHAR(20)",
        "is_primary": "否",
        "comment": "HIS科室名称",
        "nullable": "否"
      }
    ]
  }
]
```

---

## 格式验证清单

- [x] 返回数组格式（支持单表和多表）
- [x] 每个表使用 `table_info` + `columns` 结构
- [x] `table_info` 包含 `name_zh` 和 `name_en`
- [x] `columns` 是数组，每个元素包含：
  - [x] `name_zh` - 字段中文名
  - [x] `name_en` - 字段英文名
  - [x] `data_type` - 数据类型（含长度）
  - [x] `is_primary` - 是否主键
  - [x] `comment` - 注释内容
  - [x] `nullable` - 是否可空
- [x] 使用JSON数组格式，不使用表名作为key
- [x] 表名和字段名保持原始大小写

---

## 使用方法

### 调用接口

```bash
# 方式1：上传SQL文件
curl -X POST http://localhost:5000/api/resource/ddl/parse \
  -F "file=@科室对照表_原始.sql"

# 方式2：JSON请求
curl -X POST http://localhost:5000/api/resource/ddl/parse \
  -H "Content-Type: application/json" \
  -d '{"sql": "CREATE TABLE ..."}'
```

### Python调用

```python
from app.core.llm.ddl_parser import DDLParser

# 读取SQL文件
with open('科室对照表_原始.sql', 'r', encoding='utf-8') as f:
    sql_content = f.read()

# 解析DDL
parser = DDLParser()
result = parser.parse_ddl(sql_content)

# 结果格式（数组）
# [
#   {
#     "table_info": {...},
#     "columns": [...]
#   }
# ]
```

---

## 文件修改清单

1. **app/core/llm/ddl_parser.py**
   - 修改 `parse_ddl()` 方法（第86-140行）
   - 修改 `_optimize_ddl_prompt()` 方法（第251-289行）

2. **接口路由**
   - `app/api/data_resource/routes.py` - `/ddl/parse` 接口（第602行）

---

## 更新时间

2024-11-27

