原始 SQL
修改后的 SQL

SQL Diff:在线比较两个 SQL 文件

粘贴、格式化并并排比较两个 SQL 查询或 schema 文件。支持 PostgreSQL、MySQL、SQL Server、SQLite 和 Oracle。

什么是 SQL diff 工具?

一款免费的浏览器内工具,用于比较两个 SQL 文件。将旧的 CREATE TABLE 粘贴在左侧,新版本粘贴在右侧,差异会逐字符高亮显示。适用于分析查询的两个版本、ORM 生成的语句,或 pg_dump 的输出。所有数据均在本地处理,不会离开你的机器。

diff 在文本层面运行,不会将 SQL 解析为 AST,也不理解语义等价性:SELECT a, bSELECT b, a 在 diff 中会被视为不同,即使两者返回相同的行(只是顺序不同)。对于 95% 的代码审查任务(schema 迁移、查询重构、ORM 输出比对),文本 diff 正是你所需要的——列和子句的顺序本身就是团队阅读变更的一部分。

如果你曾经试图在一个 400 行的分析查询中找出那一个新增的 WHERE 子句(正是它导致了行数变化),这个工具能让你在几秒内定位到它。处理非 SQL 文本时,我们的 文本 diff 工具 是更合适的选择。对于 JSON 格式的 ORM 结果负载,JSON diff 能更清晰地处理对象重排。对于以 XML 元数据格式写成的旧版 DDL 导出,XML diff 更为适合。

diff 的实际工作原理

底层 diff 在字符级别运行,随后经过语义处理,将高亮位置调整到标识符、关键字和子句边界,而非随机的标点符号。右侧的插入内容以绿色显示,左侧的删除内容以红色显示。每个面板都有一个格式化按钮,可将 SQL 整理为统一缩进、每行一条语句的格式,有效消除比较前的格式噪声。

SQL 是一个方言族,而不是单一语言。ISO/IEC 9075 标准定义了核心语法,但每种数据库都有各自的扩展:PostgreSQL 有美元引号字符串和 RETURNINGMySQL 有反引号标识符和 ON DUPLICATE KEY UPDATESQL Server 使用 TOP 和方括号标识符,SQLite 几乎什么都能接受,而 Oracle 有 ROWNUMCONNECT BY。跨方言进行 diff 完全没问题,工具不会提示某个片段在某个方言中无效。

另一点需要了解:比较查询的文本与比较查询的执行计划不是一回事。文本相同的两条语句在不同统计信息下可能产生截然不同的执行计划,而外观不同的两条语句也可能产生完全相同的计划。对于计划比较,正确的工具是 psql 中的 EXPLAIN ANALYZE,或你所用数据库客户端(DataGrip、DBeaver、SSMS)中的等效功能。对于文本重构审查,diff 才是你需要的。关于 SQL 语言本身的背景资料,可参阅 Wikipedia

三步完成 SQL 比较

两个文本面板,一个 diff。无需注册,无需上传,无需服务器往返。

  1. 1

    粘贴或上传你的 SQL

    将旧版 SQL 粘贴在左侧,新版粘贴在右侧。也可以点击任意一侧的上传按钮,直接加载 .sql.ddl.psql 文件。如果想先看看工具的效果,点击示例按钮会用一个小型 orders schema 示例填充两个面板。

  2. 2

    格式化两侧以确保公平比较

    点击每个面板上的格式化按钮,将 SQL 整理为统一缩进、每行一条语句、关键字大写的格式。这样可以消除一侧是 DataGrip 格式化的查询、另一侧是手写查询所带来的噪声,让 diff 专注于真正的 schema 和子句变化,而不是空白字符和大小写差异。

  3. 3

    阅读 diff 结果

    左侧以红色高亮显示删除内容,右侧以绿色高亮显示插入内容。滚动任意一侧,另一侧会同步跟随。每个面板头部的变更计数会告诉你 diff 在列、JOIN、谓词和 DDL 元素中发现了多少处独立的编辑。

SQL diff 最适合哪些场景

应用前审查 schema 迁移

一个迁移 PR 新增了三列并调整了两个索引。将旧的 CREATE TABLE 与新版本进行比较,新增内容立刻显现:添加了一列 currency CHAR(3) NOT NULL,但没有设置 DEFAULT——这在任何已有行上都会失败。在迁移对生产环境执行之前发现这个问题,而不是等到凌晨 2 点部署告警响起后才发现,这正是此工作流的全部意义。对于将列类型从 VARCHAR(255) 缩小为 VARCHAR(50)ALTER TABLE 语句,同样有效。

对比分析查询的两个版本

昨天返回 12,400 行的报表查询,今天只返回 8,900 行。将昨天保存的查询与今天的版本进行 diff,问题所在立刻浮现:一个 LEFT JOIN 悄悄变成了 INNER JOIN,或者有人为了清理仪表板而添加了 WHERE status <> 'cancelled'。文本 diff 能在几秒内定位到那一行,而在 Slack 中并排阅读两个查询则要花上十分钟。

比较不同应用版本间 ORM 生成的 SQL

升级 Hibernate 或 SQLAlchemy 后,原本只有三个 JOIN 的查询现在变成了四个并带有额外子查询,或者参数绑定从 ? 换成了 $1 占位符。从各版本应用的查询日志中提取 SQL(psql 的 log_statement = all、MySQL 慢查询日志,或 APM 工具),然后进行 diff。别名名称会有些嘈杂(t1t2generatedAlias0),但真正的结构性变化通常是 diff 中唯一被标记为实际编辑的内容。

验证 DDL 重构是否保留了原有意图

你将 user_orders 重命名为 orders,并将一个 JSON blob 列拆分为规范化的表。将重构前后的 pg_dump --schema-only 输出进行 diff,就能清楚地看出是否保留了每一个约束、索引和默认值。需要警惕的陷阱是:NOT NULL 在重命名过程中悄悄消失,或者 UNIQUE 约束因为没有人在新表上重新添加而丢失。

检查预发布环境 schema 是否与生产环境一致

对预发布和生产环境分别执行 pg_dump --schema-only --no-owner,然后对两者进行 diff。差异应该恰好就是下次部署排队等待的迁移内容。其他任何差异——多出的索引、不同的列类型、残留的测试表——都是 schema 漂移的信号,在实际部署时会造成麻烦。MySQL 可使用 mysqldump --no-data,SQL Server 可使用 SCRIPT TO,或使用 DataGrip 的 schema 导出功能。

审查方言移植(Postgres 到 MySQL)

将查询从 PostgreSQL 移植到 MySQL 或反向移植,需要追踪哪些函数、类型和子句需要修改:SERIAL 对比 AUTO_INCREMENTNOW() 对比 CURRENT_TIMESTAMPRETURNING 对比 LAST_INSERT_ID()。将原版与移植版本进行 diff,所有方言替换都会一览无余。diff 不会验证 SQL 在目标方言中是否有效,因此仍需通过 psql 或 mysql 运行确认,但逐行视图能告诉你哪些决策需要再次检查。

SQL 快速参考

针对此 diff 工具最常暴露的方言和解析边界情况的简明速查表。内容基于 ISO SQL 标准及主要数据库厂商文档。

TopicWhat this tool does
方言漂移PostgreSQL、MySQL、SQL Server、SQLite 和 Oracle 各自在 ISO SQL 标准基础上添加了自己的语法扩展。SERIALAUTO_INCREMENTIDENTITYGENERATED ALWAYS AS IDENTITY 是同一概念的四种写法。
标识符大小写折叠PostgreSQL 将未加引号的标识符折叠为小写,因此 Usersusers 指的是同一张表。MySQL 的行为取决于 lower_case_table_names 设置和底层文件系统。SQL Server 默认不区分大小写,但遵循排序规则。使用 "Users"(标准)、反引号(MySQL)或方括号(SQL Server)对标识符加引号以保留大小写。
关键字大小写SQL 关键字在所有数据库中均不区分大小写,SELECTselect 对解析器而言完全相同。惯例是关键字大写、标识符小写;大多数风格指南和 ISO 规范都遵循这一惯例。混合大小写虽然有效且可以执行,但 sqlfluff 和 sqlfmt 等工具会对其进行规范化。
注释风格两种形式:-- 行注释(在行尾终止)和 /* 块注释 */(标准 SQL 中不支持嵌套,但某些方言允许)。MySQL 还支持 # 作为行注释的扩展语法。diff 将注释视为普通文本,注释的编辑会像其他任何变更一样显示出来。
语句分隔符分号(;)在标准 SQL 中用于终止一条语句。大多数客户端要求在脚本中的多条语句之间使用它。某些客户端(SSMS)使用 GO 作为批处理分隔符,但这不是 SQL 语法的一部分,而是客户端指令。
美元引号字符串(PostgreSQL)PostgreSQL 支持 $tag$ ... $tag$ 语法的字符串字面量,无需在内部对单引号进行转义,在函数体中尤为有用。$$ SELECT 'hello' $$ 是合法且常见的写法,常见于 CREATE FUNCTION 块中。其他方言不解析此语法。
参数绑定占位符没有统一标准。PostgreSQL 及面向它的 ORM 使用 $1$2。JDBC、MySQL 和 ODBC 使用 ?。命名占位符 :name 在 Oracle 和许多 ORM 字符串模板中很常见。跨版本的 ORM 生成 SQL 通常只在占位符风格上有差异,diff 前请先格式化两侧内容。
编码UTF-8 是 2026 年 SQL 文件的通用默认编码。较旧的 Windows 环境下编写的脚本可能仍以 UTF-16 LE(带 BOM)保存,这会在文本 diff 的第 1 行显示为幽灵字符。如果两个文件看起来完全相同,但 diff 在开头标记了一个字符的变化,请怀疑 BOM 的存在,并将文件重新以明确的 UTF-8 格式保存。

SQL diff 常见问题

这与对两个查询分别运行 EXPLAIN ANALYZE 有什么不同?

两者处于栈的不同层次。EXPLAIN ANALYZE 显示的是查询的执行计划——优化器根据现有统计信息决定如何执行。本工具对比的是查询的文本——也就是你代码库中实际发生变化的内容。通常你两者都需要:先用 diff 发现重构点(JOIN 类型改变了、谓词移动了),再用 EXPLAIN ANALYZE 确认优化器为新的查询形态选择了合理的执行计划。两者相辅相成,而非互相替代。

diff 是否能识别方言差异(PostgreSQL vs MySQL vs SQL Server)?

不能。diff 将输入视为纯文本,不会对 PostgreSQL、MySQL、SQL Server、SQLite 或 Oracle 进行差异化解析,也不会标记方言不兼容的语法。这是有意为之的设计:它意味着你可以跨方言进行 diff,而这正是移植查询时你想要的。如果需要方言感知的 lint 检查,可以单独使用 sqlfluff 或数据库自带的解析器。对于"这个查询改了什么"的审查场景,文本层面的粒度是正确的选择。

工具会帮我格式化 SQL 吗?

会。每个面板上的格式化按钮会将 SQL 整理为统一缩进、关键字大写、每行一条语句的格式。这是一个基础格式化工具,不是 sqlfmt 或 sqlfluff 的替代品:它不会将隐式 JOIN 改写为显式 JOIN,也不会强制执行特定方言的风格指南。目的是在 diff 之前消除格式噪声,让 DataGrip 格式化的查询和手写的查询能够干净地对比。

它会规范化关键字大小写(SELECT vs select)吗?

格式化按钮会将关键字转为大写,这是大多数风格指南和 ISO SQL 标准的惯例。如果不点击格式化,混合大小写会被视为差异,因为底层引擎基于字符级别。SQL 关键字在所有数据库中都不区分大小写,所以 selectSELECT 执行结果完全相同。标识符的大小写是另一个问题,各数据库处理方式不同,详见下方快速参考。

如何处理 ORM 生成的别名,如 t1、t2、generatedAlias0?

ORM 输出的别名天生就很嘈杂:Hibernate 生成 generatedAlias0generatedAlias1,SQLAlchemy 使用 anon_1anon_2。如果两个 ORM 版本以不同顺序分配了相同的别名,文本 diff 会将每个别名替换都标记为变更,即便查询在结构上完全相同。实际的解决办法是:格式化两侧内容,忽略仅涉及别名的差异(通常在同一行呈现为成对的红绿区块,很容易识别),专注于真正的结构性编辑——新增的 JOIN、不同的 WHERE 子句、被删除的列。

粘贴的 SQL 有大小限制吗?

有软限制,没有硬限制。diff 在浏览器中运行,上限取决于浏览器内存和你愿意等待的时间。在现代笔记本上,5,000 行的 pg_dump 输出 diff 用时远不到一秒。20 万行的 dump 可能需要几秒,并可能在低端设备上触及内存限制。对于如此大的数据库 dump,正确的工作流是先只 diff schema(pg_dump --schema-only),如需数据层面的比较再深入到具体表。

为什么同一个查询在 PostgreSQL 和 MySQL 之间显示为不同?

因为这两种方言确实存在差异,而 diff 是基于文本的,不进行解析。仅标识符引号就有三种风格:PostgreSQL 使用 "users",MySQL 使用反引号,SQL Server 使用 [users]。分页语法差异更大:PostgreSQL 和 MySQL 使用 LIMIT/OFFSET,SQL Server 使用 TOP nFETCH FIRST n ROWS ONLY,Oracle 传统上使用 ROWNUM。PostgreSQL 的 RETURNING 对应 SQL Server 的 OUTPUT,而 MERGE 语法在各厂商之间也不尽相同。diff 会如实高亮所有这些差异,这正是移植时你想要的效果。如果需要语义等价性,可通过 sqlfluff 指定正确方言后使用 sqlfluff fix 对两侧进行规范化处理。

可以 diff 两个 pg_dump 或 mysqldump 的输出吗?

可以,文本 diff 能处理 dump 文件,但原始的 pg_dumpmysqldump 输出包含易变字段,会掩盖你真正关心的结构性变化:顶部的时间戳注释、序列当前值行,以及在不同时间点捕获时会变化的 ALTER TABLE ... OWNER TO ... 语句。使用 pg_dump --schema-only --no-owner --no-acl 可去除所有权和权限信息。MySQL 的等效命令是 mysqldump --no-data --skip-comments。对于迁移审查,最佳 diff 对象是仓库中两个连续迁移脚本,而不是两个完整的 schema dump。

这个工具免费吗?需要注册吗?

完全免费,无需账号。打开页面,粘贴 SQL,开始比较。没有登录墙,没有使用上限,没有填写邮箱的表单。工具完全在浏览器中运行,不会向服务器发送任何内容——这也意味着一旦页面加载完成,离线也可以使用。

与其他在线 SQL diff 工具有什么不同?

大多数在线 diff 工具是没有 SQL 感知能力的通用文本比较器。本工具在 diff 之前增加了格式化步骤,对缩进和关键字大小写进行规范化,让结果能高亮真正的 ALTER TABLE 变化和 WHERE 子句编辑,而不是空白字符噪声。同时还能处理大型输入——5,000 行的迁移脚本 diff 用时不到一秒。

可以比较上传的两个 SQL 文件吗?

可以。点击任意面板上的上传按钮,从本地加载 .sql.ddl.psql 文件。文件内容会出现在面板中,diff 立即更新。没有任何内容会传输到服务器,文件由浏览器在本地读取。

可以同时比较两个以上的 SQL 脚本吗?

工具一次只比较两个输入——左侧和右侧。对于三方合并(例如一个公共祖先加上两个功能分支),需要分别运行两次 diff:祖先 vs 分支 A,然后祖先 vs 分支 B。这与大多数 Git 客户端处理迁移文件合并冲突时的方式相同。

粘贴包含数据库名称或表数据的 SQL 安全吗?

安全。diff 在浏览器中运行,没有 SQL 会离开你的机器。这适用于包含真实表名的 schema 文件、存储过程体,甚至包含生产数据的示例 INSERT 行。没有日志,不对粘贴内容进行分析,没有云端往返。

隐私与工作原理

你的 SQL 永远不会离开浏览器。格式化和 diff 均在你的本地机器上运行。不对输入内容进行分析,没有日志,没有"贴心"的云端往返——当你粘贴的 SQL 包含真实的表名、列名或示例行中的生产数据时,这一点尤为重要。SQL 语言本身记录于 ISO/IEC 9075,方言参考资料分别是 PostgreSQLMySQLSQL ServerSQLite