多数据库SQL速查表:SQL Server/MySQL/PostgreSQL/Oracle高频语法与优化对照
发布时间:2026/9/26 12:41:17 锦皓数字建站

做数据库开发这些年我一直有个困扰SQL语法在不同数据库里总有些“微妙”的差异。同一句查询SQL Server能跑MySQL就报错同样的逻辑Oracle和PostgreSQL的写法又不一样。每次换库都要重新查资料很烦。这次干脆把工作中所有用得上、踩过坑的SQL场景全部整理成一份完整版速查表覆盖SQL Server、MySQL、PostgreSQL、Oracle四大主力数据库从基础查询、去重清洗、窗口函数到慢SQL优化、SQL注入防护、连接串问题排查能想到的都放进来了。这份东西适合每天跟数据库打交道的后端开发、数据分析师、运维朋友也适合那些SQL基础不差但偶尔记不住语法差异的朋友。不是我吹这份速查表我自己用了小半年每次写复杂查询都靠它救命。1. 速查表设计思路1.1 为什么要做多数据库适配我接手过的项目里数据库环境往往很混杂。核心生产库是SQL Server报表库是MySQL还有一套给客户演示用的PostgreSQL偶尔还要连Oracle做数据同步。最痛苦的不是SQL本身写不出来而是同一个功能换了数据库就得换写法。我数过光分页查询这一个功能四种数据库就有四种写法SQL Server用OFFSET...FETCHMySQL用LIMITPostgreSQL也是LIMIT而Oracle在12c之前还得用ROWNUM子查询。字符串拼接更崩溃SQL Server用加号MySQL有CONCATPostgreSQL和Oracle用双竖线。这些差异不整理成对照表每次换环境都是在折磨自己的记忆。所以这份速查表的核心设计原则就是用“同一语义多库写法”的对照结构把高频SQL需求全部铺开。一个功能一列数据库并排放看上哪条抄哪条。这样做的好处是你在SQL Server上写顺手的逻辑能快速迁移到别的库里反过来遇到别人写在MySQL里的脚本你也可以迅速翻译成当前库能跑的版本。这种做法在团队协作里特别实用尤其是需要跨库同步数据的时候照着对照表翻译脚本基本不会出错。1.2 全场景覆盖的板块划分整个速查表按使用频率分了六大块。第一块是日常查询包括SELECT、WHERE、ORDER BY、分页、聚合这些最基础的操作。第二块是进阶数据处理比如去重、窗口函数、字符串和日期处理。第三块是数据管理涵盖增删改、表结构变更、默认值设置、跨库拷贝表。第四块是性能优化重点解决慢SQL和参数配置问题。第五块是安全与连接包括SQL注入防护和SSL连接报错这些偏运维的场景。最后一块是安装部署与排错因为我在实际工作中发现很多问题根本不是SQL本身写错了而是环境没配好。这样划分的逻辑很简单一次写查询的时候按“查数据 → 处理数据 → 改数据 → 查性能 → 排故障”这条链路翻表基本能覆盖九成以上的场景。我自己用下来感受很深的一点是与其把速查表写成一本完整的事无巨细的SQL教科书不如做成“高频场景 典型写法 避坑提示”的组合因为很多人查速查表的场景要么是赶工期要么是线上出了问题急着处理这时候看一眼表格直接拿答案就是最高效的。2. 高频查询语句对照2.1 基础查询与分页的五种写法先上最实用的部分日常查询的写法对照。我用一个表格总结这些高频操作在四大数据库中的差异这是整个速查表里被翻得最多的部分。场景SQL ServerMySQLPostgreSQLOracle取前N行SELECT TOP 10LIMIT 10LIMIT 10FETCH FIRST 10 ROWS ONLY分页第11-20行OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLYLIMIT 10 OFFSET 10LIMIT 10 OFFSET 10OFFSET 10 FETCH NEXT 10 ROWS ONLY12c字符串拼接a bCONCAT(a, b)a || ba || b当前日期时间GETDATE()NOW()NOW()SYSDATE空值替换ISNULL(col, 0)IFNULL(col, 0)COALESCE(col, 0)NVL(col, 0)自增列IDENTITY(1,1)AUTO_INCREMENTGENERATED ALWAYS AS IDENTITYIDENTITY12c这里有个细节特别提醒一下。Oracle的OFFSET...FETCH语法需要12c及以上版本如果在旧版本上跑就得用ROWNUM嵌套子查询的办法也就是先排序再用ROWNUM筛选。我在一次数据迁移项目中就吃过这个亏给客户写的分页脚本在11g上怎么跑怎么报语法错误最后查文档才发现是版本问题。所以用任何语法之前先确认目标数据库的版本这是省时间的关键一步。2.2 聚合分组与去重查询的三种手段去重这个场景很常见而且不复杂但我发现很多人在“到底用DISTINCT还是GROUP BY”这个问题上犹豫。我的经验是简单去重用DISTINCT就行比如查所有不重复的城市列表如果去重的同时要算聚合值比如每个城市有多少订单那就必须用GROUP BY如果要去重并按某列保留最新一条记录那就要用窗口函数。三种方式的适用场景完全不同可以看成递进关系。举个实际例子。假设订单表有重复数据要把相同用户ID的订单只保留最新一条正确的写法是用ROW_NUMBER()加OVER子句-- SQL Server / PostgreSQL / MySQL 8.0 WITH ranked AS ( SELECT order_id, user_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC) AS rn FROM orders ) DELETE FROM orders WHERE order_id IN ( SELECT order_id FROM ranked WHERE rn 1 );这段SQL在MySQL 5.7及更早版本上不能直接跑因为老版本不支持CTE和窗口函数。我当时做数据清洗是在MySQL 5.7上跑这种去重逻辑的只能改成派生表嵌套的写法性能还差了挺多。所以说速查表里给出现代写法没问题但生产环境版本老旧的时候必须能临场变通。2.3 字符串与日期处理差异字符串处理这块函数名在不同库里经常对不上号。最典型的是截取字符串SQL Server用SUBSTRINGMySQL和PostgreSQL用SUBSTRING但Oracle要用SUBSTR少一个字母。日期格式化更是各有各的套路SQL Server的FORMAT函数功能强但性能差我用它格式化过十万行数据的日期列结果跑了三秒多换成CONVERT之后毫秒级出结果。MySQL的DATE_FORMAT比较稳定PostgreSQL的TO_CHAR非常灵活Oracle的TO_CHAR也算标配。场景SQL ServerMySQLPostgreSQLOracle截取字符SUBSTRING(col, 1, 5)SUBSTRING(col, 1, 5)SUBSTRING(col, 1, 5)SUBSTR(col, 1, 5)格式化日期FORMAT(col, yyyy-MM-dd)DATE_FORMAT(col, %Y-%m-%d)TO_CHAR(col, YYYY-MM-DD)TO_CHAR(col, YYYY-MM-DD)字符串长度LEN(col)CHAR_LENGTH(col)LENGTH(col)LENGTH(col)去除首尾空格LTRIM(RTRIM(col))TRIM(col)TRIM(col)TRIM(col)3. 进阶场景补全3.1 窗口函数在不同数据库里的写法与坑窗口函数已经成为SQL进阶绕不开的话题。ROW_NUMBER、RANK、DENSE_RANK、SUM OVER这组函数我从一开始的“看不懂”到现在的“离不开”中间踩了不少坑。先说它们各自的分工ROW_NUMBER给每行一个唯一的递增序号RANK遇到相同排名字会跳号比如两个并列第二之后直接跳到第四DENSE_RANK不跳号并列第二之后是第三。排名场景用RANK还是DENSE_RANK取决于业务上是否允许名次空缺。用法上各大数据库的核心语法一致都是“函数() OVER (PARTITION BY 分组列 ORDER BY 排序列)”但版本限制不一样。SQL Server 2008就开始支持窗口函数MySQL要到8.0PostgreSQL和Oracle在很早就支持了。我在给一个MySQL 5.7的报表系统做排名时就因为没有窗口函数不得不改成用户变量模拟写法长了一倍不说可读性还差。所以要列速查表的话版本信息必须标清楚不然别人照着写还是跑不了。3.2 判断数字字符串的N种方案判断一个字段是否为数字字符串这是经常被翻出来的经典需求。SQL Server里很多人第一反应是用ISNUMERIC但这个函数特别坑ISNUMERIC(1e5)返回1因为科学计数法也被当成数字ISNUMERIC($100)也返回1因为货币符号也被接受。更夸张的是ISNUMERIC(-)在某些版本上也返回1。我排查过一次脏数据就是这个函数把一堆特殊字符放行了最后全部被吃掉。要给安全可靠的判断我的建议是优先用TRY_CAST或TRY_CONVERT写法是这样-- SQL Server 2012 SELECT CASE WHEN TRY_CAST(col AS DECIMAL(18,2)) IS NULL THEN 0 ELSE 1 END AS is_number FROM table_name;MySQL里判断数字字符串正则表达式是最稳的-- MySQL SELECT CASE WHEN col REGEXP ^[0-9]$ THEN 1 ELSE 0 END AS is_number FROM table_name;PostgreSQL和Oracle可以用正则也可以用TRANSLATE或者REGEXP_LIKE。DB2则可以用TRANSLATE函数把非数字字符替换成空串再判断长度。这几种方案的共性是绕开“看似好用实则心机很深”的默认函数用严格模式去校验输入。3.3 数据清洗去重实操记录数据清洗里最烦人的就是“看起来很对实际上重复”的数据。我上个月刚帮业务部门处理了一个会员表两万多条数据里头有三千多条重复的按邮箱去重后只能剩下一万多条。我用的是ROW_NUMBER PARTITION BY的做法先按邮箱分组再用注册时间排序保留每个邮箱里最早注册的那条记录。整个操作分两步走第一先查出重复记录清单确认数量第二再执行删除。这类操作一定要养成先备份的习惯。我通常先把要删除的数据导到一张临时表万一手误删错了还能恢复。比如删除前可以执行SELECT * INTO deleted_records FROM ranked WHERE rn 1;这样就把所有会被删掉的行备份下来了。做删除类操作的时候“留一手”这个习惯能救命我确实因为一次没备份差点把生产环境数据搞没当时整个人都懵了。4. 数据操作与管理4.1 增删改与批量执行的实用写法INSERT、UPDATE、DELETE这三个操作本身差异不大但真正做事的时候讲究的是效率和安全性。比如批量插入SQL Server用INSERT INTO ... VALUES (...), (...)MySQL也可以用同样的写法但PostgreSQL和Oracle需要VALUES列表搭配SELECT来扩展PostgreSQL用INSERT INTO ... SELECT * FROM ...Oracle则可以用INSERT ALL。这些差异不查还真写不对。C#里执行多条SQL语句也是我常被问到的。其实SqlCommand的CommandText可以直接写多条用分号分隔的语句跑完会依次执行。但如果想要一次执行同时拿回多个结果集就得用SqlDataReader.NextResult往下翻不然只能拿到第一个查询的结果。这在一次性初始化多张表数据时特别有用能减少网络往返把几十次数据库请求合并成一次。4.2 GUID默认值与自增主键的选择热搜词里“SQL默认值GUID”说明这个话题热度一直不低。SQL Server里给列设置GUID默认值很简单默认值写NEWID()生成的是普通GUID写NEWSEQUENTIALID()生成的是有序GUID。MySQL则用UUID()或UUID_SHORT()。GUID做主键最大的好处是全局唯一分布式合并数据不用考虑冲突缺点是随机生成的GUID会让页分裂严重索引碎片率飙升因为GUID不是顺序增长的。所以如果表的数据量比较大、又是核心业务表我更倾向于用自增整数做主键再给业务字段单独建唯一索引。但如果涉及多表合并或者离线同步那GUID就是更稳妥的选择省去了处理主键冲突的麻烦。NEWSEQUENTIALID就是微软为了补足GUID随机性问题做的方案有顺序性但又要全局唯一兼顾了两边。选哪种没有绝对对错要按业务场景来。4.3 跨库拷贝表的几种方法从一个数据库往另一个数据库导表这个需求太常遇到了。SQL Server里最简单的做法是SELECT INTO一条语句就能在同服务器环境下创建新表并拷贝数据SELECT * INTO target_db.dbo.backup_table FROM source_db.dbo.original_table;如果目标表已经存在就用INSERT INTO ... SELECTINSERT INTO target_db.dbo.backup_table SELECT * FROM source_db.dbo.original_table;如果源库和目标库不在同一台服务器上那就得走链接服务器或者用SSMS自带的导入导出向导。我导过几次上亿行的大表这种情况下SELECT INTO也没那么靠谱最好还是用临时表分批迁移或者直接上ETL工具。跨库拷表最容易踩的坑是字段类型不兼容比如源库的VARCHAR到了目标库变成了NVARCHAR字符集一变内容可能会乱尤其是中文数据。所以导完后一定要做数据抽查不能导完就以为万事大吉。5. 慢SQL优化实战5.1 慢SQL排查的标准流程遇到查询慢第一件事不是去改SQL语句而是先定位瓶颈。我的排查顺序是先看是不是全表扫描有没有走索引再看连接条件里是不是有隐式转换接着看是否因为函数包裹了索引列导致索引失效最后检查是不是统计信息过期导致优化器选错了执行计划。拿SQL Server举例我一般直接跑一下看执行计划重点看有没有出现Table Scan或Index Scan以及流失估计是否相差悬殊。索引失效的常见原因其实很好记WHERE条件里对索引列用了函数或计算比如WHERE YEAR(create_time) 2024这样索引就废了。正确写法是WHERE create_time 2024-01-01 AND create_time 2025-01-01让优化器能直接运用索引范围扫描。还有隐式转换的问题比如字符串列和数字比较数据库默认把字符串转成数字索引又废了。这些都是慢SQL的“重灾区”在往速查表里整理优化条目时我把这些经验单独开了一栏“索引失效的七个元凶”每次排查对照着看效率提升很明显。5.2 并行SQL优化与参数调整SQL Server在跑复杂查询时优化器会考虑用多线程并行执行。但并行并不总是好事我在一张千万级订单表的统计查询里就见过并行线程数飙到很高CPU跑满但总执行时间反而比单线程还慢。这种情况通常是并行度设置太高或者计划被错误估计了导致大量线程做无用的任务切换。调整并行度上限可以用EXEC sp_configure max degree of parallelism, 4; RECONFIGURE;并行操作还要留意CXPACKET这种等待类型出现它说明查询子在等待并行线程同步如果频繁出现且单次等待时间很长就要评估是调整MAXDOP还是优化SQL本身让它走更简单的执行计划。我的一般做法是先加索引看能不能把查询降到单线程跑实在降不下来再碰并行参数。快去动MAXDOP是治标不治本核心还是要让查询简单起来。5.3 SQL Server内存参数调整与Windows NT占用热搜词里“SQL Server Windows NT占用内存”其实是很多人对SQL Server内存管理机制的误解。SQL Server启动后会尽量把能用的内存都预分配掉也就是我们看到任务管理器里那个SQL Server进程占了几十G内存。这是SQL Server按设计来的它不想频繁从操作系统申请和释放内存。但如果机器上还跑着别的应用就必须限制一下常用的做法是设置最大服务器内存EXEC sp_configure show advanced options, 1; RECONFIGURE; EXEC sp_configure max server memory, 16384; -- 单位MB就是16GB RECONFIGURE;我在一台32GB内存的服务器上给SQL Server设置了24GB剩下的留给操作系统和其他服务跑了一个多月再也没有出现过内存告警。这里强调一遍不设置max server memory的SQL Server会一直吃内存吃到操作系统报警为止这不是SQL Server“出毛病了”而是它的默认策略就是“内存能占就占用起来省事”。所以生产环境一定要主动配置早配早安心。6. 安全与连接问题排查6.1 SQL注入原理与万能密码绕过热搜里“SQL注入万能密码绕过”这个词我必须展开讲因为这就是最典型的数据库安全漏洞场景。所谓万能密码核心原理是在登录SQL里用字符串拼接拼出改变逻辑的值。假设登录SQL长这样SELECT * FROM users WHERE username admin AND password 123456;攻击者在密码框里输入 OR 11 --拼出来的SQL就变成了SELECT * FROM users WHERE username admin AND password OR 11 --;因为OR后面跟了恒真条件整条WHERE条件变成永真绕过密码校验直接登录成功。这在我早期做过的一个外包项目里真实发生过当时业务方反馈后台被莫名其妙登录了排查后就是登录接口直接拼接字符串导致的。从那次之后我再也没写过一条拼接SQL全部改成参数化查询。using (var cmd new SqlCommand(SELECT * FROM users WHERE username u AND password p, conn)) { cmd.Parameters.AddWithValue(u, username); cmd.Parameters.AddWithValue(p, password); // 执行 }参数化查询的原理是数据库会把SQL结构和参数值分开处理参数值永远不会被当成SQL代码解析。这是阻断SQL注入最根本的手段没有之一。6.2 SSL加密连接错误的排查记录“驱动程序无法通过使用安全套接字层(SSL)加密与SQL Server建立安全连接”这个报错我在给一个Java项目配SQL Server连接时碰到过。当时一直连不上看了驱动版本又看了连接串折腾半天才定位到问题。这个报错的核心原因是SQL Server用了自签名证书而JDBC驱动默认要求加密且校验证书自签名证书不在受信列表里驱动就拒绝连接。解决办法有两条路。一条是给SQL Server配置正式证书生产环境推荐这么做让数据库服务自己拿着合法证书加密通信。另一条是在连接串里加trustServerCertificatetrue跳过证书校验适合开发测试环境但在生产环境等于裸奔强烈不建议。连接串示例jdbc:sqlserver://localhost:1433;databaseNamemydb;encrypttrue;trustServerCertificatetrue顺带提一句SQL Server 2019和2022默认就开启了加密连接很多老项目的连接串没更新升级数据库版本之后突然连不上就是这个套路。遇到这种报错先看版本有没有变再看证书信不信任。6.3 安装版本选择与常见排错SQL Server的安装镜像版本很多从2008 R2到2016、2019、2022都有。我的建议是新项目直接用开发版或者Express版起步Express版免费且适合学习。安装时如果提示“对密钥无访问权限”十有八九是当前用户权限不够右键以管理员身份运行安装程序就能解决。另外“2008和SSMS 2022共存吗”这个问题我经常被问到——SSMS是客户端工具和数据库实例是独立的2008实例完全可以用SSMS 2022去连接管理但这只是接口兼容不会把2008的引擎升级。安装版本选择还有一个点SQL Server 2019和2022的安装包很大下载之前建议确认系统盘有没有足够空间。我之前给一台新服务器装2019C盘只剩15GB结果安装到一半提示磁盘空间不足整个实例装到一半还得推出非常难受。安装前把所有路径都选到数据盘能省去很多麻烦。7. 常见问题速查表最后把我遇到过的高频问题整理成一张速查表按“症状 → 原因 → 解决方案”的格式放出来方便大家直接查。症状可能原因解决方案SQL Server连接报SSL相关错误证书不受信任生产配正式证书开发加trustServerCertificatetrue查询结果相同但执行极慢统计数据过期或索引失效更新统计信息用实际查询优化写法删除重复数据后要恢复操作前没备份删除前SELECT INTO备份到临时表MySQL老版本不支持窗口函数版本低于8.0用派生表或用户变量替代SQL Server内存占用90%以上未设置max server memory执行sp_configure设置上限建表默认值要GUID需要用NEWID()或NEWSEQUENTIALID()按主键使用场景选用数据库备份在低版本恢复失败备份文件版本高于目标实例版本还原到同版本或更高版本实例SELECT TOP用不了报错数据库不是SQL Server换成LIMIT或FETCH FIRST字符串拼接用了加号但得到数字隐式转换确认拼接列是字符类型转成VARCHAR再拼批量执行SQL只返回了第一个结果集DataReader没用NextResult用NextResult循环取结果集我现在回想这份速查表能坚持用下来的原因不是它记了多少函数而是它把我每次踩坑后的总结都沉淀下来了。比如TRY_CAST判断数字、NEWSEQUENTIALID做主键、max server memory必须设置这些经验一开始也不是从文档里读来的而是线上出问题之后一点点复盘出来的。所以也建议你拿到这份表后按自己的项目情况做增改把那些真正坑过你的SQL问题补进去。过个半年回来看会发现你沉淀下来的那份表才是最适合自己的速查表。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。