Codex 与数据库交互实战:用 AI 生成高效 SQL 与自动化管理数据库

引言

在日常开发中,数据库操作占据了相当大的工作量——编写 SQL 查询、设计表结构、编写 ORM 模型代码、优化慢查询等等。Codex 作为 OpenAI 开源的 AI 编码 CLI 工具,不仅能帮你写业务逻辑代码,在数据库相关任务上同样表现出色。本文将深入探讨如何利用 Codex 高效完成数据库相关的开发工作,从 SQL 生成到模式迁移,再到性能优化,覆盖数据库开发的完整链路。

1. 用自然语言生成 SQL 查询

Codex 最直接的应用场景就是用自然语言描述需求,让它生成对应的 SQL 语句。无论是简单的 SELECT 还是复杂的多表 JOIN + 子查询,Codex 都能准确理解意图。

场景一:复杂联表查询

假设有一个电商数据库,包含 users(用户表)、orders(订单表)和 order_items(订单明细表),你想查询"2025 年消费总额超过 5000 元的前 20 名用户及其最近一笔订单详情"。

在终端中进入项目目录,直接与 Codex 对话:

> 帮我写一个 SQL 查询:查询 2025 年消费总额超过 5000 元的前 20 名用户,
  以及他们各自最近一笔订单的订单号、金额和下单时间。
  表结构如下:
  - users: id, name, email
  - orders: id, user_id, order_no, total_amount, created_at
  - order_items: id, order_id, product_name, quantity, price

Codex 会生成类似这样的 SQL:

WITH user_annual_spending AS (
    SELECT
        u.id,
        u.name,
        u.email,
        SUM(o.total_amount) AS total_spent
    FROM users u
    INNER JOIN orders o ON u.id = o.user_id
    WHERE o.created_at >= '2025-01-01 00:00:00'
      AND o.created_at < '2026-01-01 00:00:00'
    GROUP BY u.id, u.name, u.email
    HAVING SUM(o.total_amount) > 5000
    ORDER BY total_spent DESC
    LIMIT 20
),
latest_order AS (
    SELECT
        o.user_id,
        o.order_no,
        o.total_amount,
        o.created_at,
        ROW_NUMBER() OVER (PARTITION BY o.user_id ORDER BY o.created_at DESC) AS rn
    FROM orders o
    WHERE o.user_id IN (SELECT id FROM user_annual_spending)
)
SELECT
    us.name,
    us.email,
    us.total_spent,
    lo.order_no AS latest_order_no,
    lo.total_amount AS latest_order_amount,
    lo.created_at AS latest_order_time
FROM user_annual_spending us
LEFT JOIN latest_order lo ON us.id = lo.user_id AND lo.rn = 1
ORDER BY us.total_spent DESC;

生成结果使用了 CTE(公用表表达式)和窗口函数,结构清晰、可读性强。你还可以进一步追问:

> 如果 users 表数据量在千万级别,这个查询有哪些优化点?帮我分析索引策略。

场景二:数据修复脚本

生产环境中经常遇到需要批量修复数据的情况。比如:"将所有 2024 年 6 月之前的待支付订单标记为已取消"。

> 写一条 UPDATE 语句,将 orders 表中 status='pending' 且 created_at 早于 '2024-06-01' 的订单状态改为 'cancelled',
  同时更新 updated_at 为当前时间。要求先输出 SELECT 确认影响行数,再给出 UPDATE。

Codex 会生成安全的二段式脚本:

-- 第一步:确认影响范围
SELECT COUNT(*) AS affected_rows
FROM orders
WHERE status = 'pending'
  AND created_at < '2024-06-01 00:00:00';

-- 第二步:执行更新
UPDATE orders
SET status = 'cancelled',
    updated_at = NOW()
WHERE status = 'pending'
  AND created_at < '2024-06-01 00:00:00';

2. 数据库模式设计与迁移

从需求描述到建表语句

Codex 可以根据业务需求直接生成建表 DDL 语句。在设计新功能模块时,这个能力特别实用。

> 设计一个"文章标签系统"的数据库表结构,需求如下:
  - 一篇文章可以有多个标签,一个标签下有多篇文章(多对多)
  - 标签需要支持层级关系(父标签和子标签)
  - 每篇文章需要记录标签关联的创建时间
  - 标签名全局唯一,且需要软删除
  - 使用 MySQL,InnoDB 引擎,utf8mb4 字符集

Codex 会输出完整的三表结构:

CREATE TABLE tags (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL COMMENT '标签名称',
    parent_id BIGINT UNSIGNED DEFAULT NULL COMMENT '父标签ID,NULL表示顶级标签',
    sort_order INT DEFAULT 0 COMMENT '排序权重',
    deleted_at TIMESTAMP NULL DEFAULT NULL COMMENT '软删除时间',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_name (name),
    KEY idx_parent_id (parent_id),
    KEY idx_deleted_at (deleted_at),
    CONSTRAINT fk_tags_parent FOREIGN KEY (parent_id)
        REFERENCES tags(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标签表';

CREATE TABLE article_tags (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    article_id BIGINT UNSIGNED NOT NULL COMMENT '文章ID',
    tag_id BIGINT UNSIGNED NOT NULL COMMENT '标签ID',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '关联时间',
    UNIQUE KEY uk_article_tag (article_id, tag_id),
    KEY idx_tag_id (tag_id),
    KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文章标签关联表';

注意 Codex 自动添加了唯一约束防止重复关联、给外键列加了索引、使用了规范的注释,这些都是良好的工程实践。

与 Laravel Migration 配合

如果你使用 Laravel 框架,可以让 Codex 直接将上述表结构转换为 Migration 文件:

> 将上面三个 MySQL 建表语句转换为 Laravel Migration,
  文件名分别为 create_tags_table 和 create_article_tags_table

Codex 会生成对应的 PHP Migration 代码:

// database/migrations/xxxx_create_tags_table.php
Schema::create('tags', function (Blueprint $table) {
    $table->id();
    $table->string('name', 50)->comment('标签名称');
    $table->unsignedBigInteger('parent_id')->nullable()->comment('父标签ID');
    $table->integer('sort_order')->default(0)->comment('排序权重');
    $table->softDeletes()->comment('软删除时间');
    $table->timestamps();

    $table->unique('name');
    $table->index('parent_id');
    $table->index('deleted_at');
    $table->foreign('parent_id')
          ->references('id')->on('tags')
          ->onDelete('set null');
});

然后在 AGENTS.md 中配置好项目的数据库连接信息,Codex 甚至能帮你直接执行 php artisan migrate

3. ORM 模型代码生成

Eloquent 模型与关联关系

Codex 能根据建表语句或业务描述自动生成 Eloquent Model,包含完整的关联关系、访问器和查询作用域。

> 根据上面创建的 tags 和 article_tags 表,生成完整的 Eloquent Model,
  包含 hasMany/hasOne 关联、parent/children 树形关联、
  软删除 trait、以及按热度排序的 scope

生成的 Tag 模型类似:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Tag extends Model
{
    use SoftDeletes;

    protected $fillable = ['name', 'parent_id', 'sort_order'];

    public function parent(): BelongsTo
    {
        return $this->belongsTo(Tag::class, 'parent_id');
    }

    public function children(): HasMany
    {
        return $this->hasMany(Tag::class, 'parent_id')->orderBy('sort_order');
    }

    public function articles(): BelongsToMany
    {
        return $this->belongsToMany(Article::class, 'article_tags')
                    ->withPivot('created_at')
                    ->withTimestamps();
    }

    public function scopePopular($query)
    {
        return $query->withCount('articles')->orderBy('articles_count', 'desc');
    }

    public function scopeRoot($query)
    {
        return $query->whereNull('parent_id');
    }
}

GORM 模型代码(Go 语言)

同样,切换到 Go 项目时,Codex 可以生成 GORM 模型:

> 将上面三个 MySQL 表转为 Go 的 GORM 模型结构体
package models

import (
    "time"
    "gorm.io/gorm"
)

type Tag struct {
    ID        uint           `gorm:"primaryKey" json:"id"`
    Name      string         `gorm:"type:varchar(50);uniqueIndex;not null;comment:标签名称" json:"name"`
    ParentID  *uint          `gorm:"index;comment:父标签ID" json:"parent_id"`
    SortOrder int            `gorm:"default:0;comment:排序权重" json:"sort_order"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`

    Parent   *Tag   `gorm:"foreignKey:ParentID" json:"parent,omitempty"`
    Children []Tag  `gorm:"foreignKey:ParentID" json:"children,omitempty"`
    Articles []Article `gorm:"many2many:article_tags;" json:"-"`
}

type ArticleTag struct {
    ID        uint      `gorm:"primaryKey" json:"id"`
    ArticleID uint      `gorm:"uniqueIndex:uk_article_tag;not null" json:"article_id"`
    TagID     uint      `gorm:"uniqueIndex:uk_article_tag;not null;index" json:"tag_id"`
    CreatedAt time.Time `json:"created_at"`
}

4. SQL 性能分析与查询优化

Codex 在数据库性能调优方面同样能发挥重要作用。你可以把 EXPLAIN 输出直接粘贴给 Codex,让它帮你分析并给出优化建议。

> 这个查询在 500 万条数据下执行超过 8 秒,帮我分析 EXPLAIN 输出并给出优化方案:

  EXPLAIN SELECT u.name, COUNT(o.id) as order_count
  FROM users u
  LEFT JOIN orders o ON u.id = o.user_id
  WHERE o.created_at >= '2025-01-01'
  GROUP BY u.id
  HAVING order_count > 10
  ORDER BY order_count DESC;

  EXPLAIN 输出:
  +----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
  | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
  +----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
  |  1 | SIMPLE      | o     | ALL  | idx_created_at| NULL | NULL    | NULL | 5234872 | Using where |
  |  1 | SIMPLE      | u     | ALL  | PRIMARY       | NULL | NULL    | NULL | 1023456 | Using where |
  +----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

Codex 会发现两个主要问题:(1)orders 表的 idx_created_at 索引未被使用;(2)LEFT JOIN 配合 WHERE 对右表过滤,实际退化成了 INNER JOIN。它会给出具体建议:

-- 修正后的查询(LEFT JOIN 改为 INNER JOIN)
SELECT u.name, COUNT(o.id) as order_count
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.created_at >= '2025-01-01'
GROUP BY u.id
HAVING order_count > 10
ORDER BY order_count DESC;

-- 建议创建的复合索引
CREATE INDEX idx_orders_user_created ON orders(user_id, created_at);

更进一步,你可以让 Codex 分析慢查询日志:

> 从 MySQL 慢查询日志中提取了以下 TOP 10 慢查询(文件路径 slow.log),
  帮我逐条分析并提供优化方案,然后生成一个 Markdown 格式的优化报告。

Codex 会通过文件读取功能分析日志内容,按查询类型分类(全表扫描、索引失效、回表过多等),输出结构化的优化报告。

5. 数据库日常管理自动化

批量生成 CRUD 接口

Codex 的 exec 命令特别适合批量任务。比如为新模块一次性生成整套 CRUD 代码:

codex exec "根据 tags 表的建表 DDL(文件在 schema.sql 中),
生成完整的 RESTful API 控制器代码:
1. GET /api/tags - 分页列表,支持 name 模糊搜索和 parent_id 筛选
2. GET /api/tags/{id} - 详情,包含子标签和文章数量
3. POST /api/tags - 创建标签
4. PUT /api/tags/{id} - 更新标签
5. DELETE /api/tags/{id} - 软删除,如果有子标签则禁止删除
使用 Laravel 框架,请求验证用 FormRequest,资源响应用 API Resource"

数据迁移与 ETL 脚本

Codex 还能帮你编写数据库之间数据迁移的 ETL 脚本:

> 我需要从旧系统的 MySQL 数据库迁移用户数据到新系统的 PostgreSQL。
  写一个 Python 脚本,使用 SQLAlchemy 连接两个数据库,
  对 users 表进行字段映射和数据类型转换,
  支持断点续传(记录最后迁移的 ID),
  并输出每分钟的迁移进度。

Codex 会生成包含批量处理、异常捕获和进度日志的完整脚本。

通过 MCP 直接操作数据库

如果你已经为 Codex 配置了数据库 MCP 服务器(参见之前的 MCP 集成文章),更可以让 Codex 直接执行数据库操作:

> 查看当前数据库中所有表的大小(按数据量降序排列)

> 找出没有设置 created_at 索引的所有表,生成 ALTER TABLE 语句

> 分析 users 表的数据分布:按注册月份统计用户数,输出为柱状图

6. 最佳实践与注意事项

始终先审阅生成的 SQL

AI 生成的 SQL 虽然多数情况下准确,但在生产环境执行前务必仔细审查。建议在 AGENTS.md 中添加规则:

## 数据库操作规则

- 任何涉及写操作(INSERT/UPDATE/DELETE)的 SQL 必须先输出预览版本,经我确认后再执行
- MySQL 环境 UPDATE 和 DELETE 必须包含 LIMIT 子句
- 生产环境禁止执行 DROP 或 TRUNCATE

提供充分的表结构上下文

Codex 不是数据库客户端,它需要你主动提供表结构信息。最佳实践是:

在项目 AGENTS.md 中列出核心表及其字段说明

对于复杂查询,在 schema.sql 文件中维护完整的建表 DDL

提问时附上相关表的 CREATE TABLE 语句片段

利用 Codex 学习 SQL

Codex 还是优秀的学习工具。遇到不熟悉的 SQL 语法时,直接问它:

> Oracle 的 CONNECT BY 和 SQL Server 的 CTE 递归查询有什么区别?
  分别给出示例,查询一个部门层级树。

> 解释一下 MySQL 的覆盖索引(Covering Index)是什么,
  为什么有时候 EXPLAIN 的 Extra 字段显示 Using index 但查询仍然很慢?

与迁移工具链配合

将 Codex 集成到你的数据库工具链中:

  • Laravel Migration 配合:生成 Migration 文件
  • goose / golang-migrate 配合:生成 Go 项目的 SQL 迁移脚本
  • Skeema 配合:生成声明式的表结构管理配置
  • gh-ost / pt-online-schema-change 配合:生成无锁 DDL 变更脚本

总结

Codex 在数据库开发领域的能力远超简单的 SQL 补全——它能够理解业务语义、设计表结构、生成 ORM 模型、分析查询性能,甚至编写完整的数据迁移脚本。将 Codex 融入日常数据库工作流,可以显著减少手工编写 SQL 和模板代码的时间,让你更专注于数据建模和架构设计。

在实际使用中,关键是提供充分的上下文信息(表结构、业务需求)并建立安全的使用规范(先审阅后执行)。掌握这些技巧后,你会发现 Codex 不仅是应用层编码的得力助手,更是数据库开发的效率倍增器。