尧图精选

SQL Server常用函数详解:日期转换、字符串处理、数学与聚合实战

🕒 发布时间:2026/10/2 3:10:23 📁 来源:尧图网络
做SQL Server开发这些年我越来越发现一个规律日常工作中真正能拉开效率差距的往往不是那些被吹得神乎其神的高级特性而是最基础的函数用得熟不熟。日期字段要不要转成字符串字符串怎么截取、怎么拼接才不踩坑分组统计的结果怎么算才是对的这些问题几乎天天都在遇到。这篇文章我把SQL Server最常用的日期转换、字符串、数学、聚合四类函数全部梳理了一遍每条都配有实际使用场景、语法示例和避坑细节适合刚入门SQL Server的新手系统学习也适合中高级开发者在日常工作中随手查阅。你可以把它当成一份可以反复回看的函数手册也可以直接照着例子复制到自己的环境里跑一遍。1. 内容整体设计与思路拆解1.1 为什么这四类函数最值得系统整理在实际的SQL Server开发中我做过报表、写过存储过程、也做过不少数据迁移的脏活累活回头总结发现一个很有意思的现象你写的每一条SQL几乎都逃不开四种操作——把时间转换成想要的格式、把文本切一切拼一拼、把数字算一算、把数据聚合起来做统计。这四种操作对应的正是日期函数、字符串函数、数学函数和聚合函数。刚入行的时候我也犯过傻喜欢把函数零零散散地记在本地笔记里需要用的时候翻出来看一眼结果过两天又忘。后来我换了个思路先把SQL Server的函数体系在脑子里梳理成一张地图把每个函数归类、做对比、看差异再配合实际业务场景去练记忆效率一下子提升了不少。这篇文章本质上就是我把自己梳理过的这张函数地图摊开给你看每个函数都有语法、有例子、有坑点照着走一遍比孤立地背公式要牢固得多。1.2 学习函数库的正确思路不少初学者学函数最大的误区是一个一个孤立地背。背LEN、背SUBSTRING、背CHARINDEX背完就忘因为不知道它们之间的组合关系。其实SQL Server函数更像是一套积木单个函数往往解决不了复杂问题组合起来才是王道。比如说“提取邮箱地址中的用户名”你至少需要CHARINDEX定位的位置再用LEFT截取前面的部分这就同时用到了定位和截取两类函数。再比如常见的“按月统计销售额”需要用到DATEPART把日期归一到月份再配合GROUP BY和SUM做聚合这又涉及日期函数和聚合函数的协作。所以我在下文的讲解中不会只列函数的定义和语法而是把每个函数的典型业务场景、容易踩的坑、和相邻函数的对比都带上。这样你看一遍就会有真实的使用记忆而不是对着文档抄完就扔。2. 日期转换与处理函数最容易踩坑的一类2.1 取当前时间的函数GETDATE、SYSDATETIME、CURRENT_TIMESTAMP先讲最基础的一类获取当前时间。SQL Server里日常用得最多的是这三个——GETDATE()、SYSDATETIME()、CURRENT_TIMESTAMP。GETDATE() 返回当前日期和时间精度到毫秒SYSDATETIME() 精度更高能到100纳秒CURRENT_TIMESTAMP 是ANSI标准写法效果等同GETDATE()我在实际项目中通常默认用GETDATE()因为它老牌、稳定团队里人人一看就懂。如果对时间精度有硬性要求比如日志表需要记录更精确的操作时间才换成SYSDATETIME()。CURRENT_TIMESTAMP一般在写跨数据库兼容脚本时使用因为它更符合SQL标准将来换数据库引擎时改动最小。这里有一个非常容易忽略的细节GETDATE()返回的是SQL Server所在服务器的本地时间。如果你的应用服务器和数据库服务器不在同一个时区或者数据库服务器有夏令时调整取出来的时间可能和你预期差好几个小时。这时候要么统一约定各端都使用UTC时间存储要么在取数时显式做时区换算千万别默认“两边时间一定一样”。2.2 CONVERT与CAST日期转字符串的核心日期转换是我见过报错最多的函数类别之一。SQL Server里最常用的两个转换函数是CAST和CONVERT。CAST是ANSI标准的写法语法简单SELECT CAST(GETDATE() AS VARCHAR(20));但CAST在日期转字符串时有个痛点——它只能输出固定的默认格式比如“2025-01-05 14:23:45”这种。如果你想要“2025年01月05日”或者“20250105”这种紧凑格式CAST就无能为力了。这时候就要用到CONVERT它比CAST多了一个样式码参数SELECT CONVERT(VARCHAR(10), GETDATE(), 23); -- 2025-01-05 SELECT CONVERT(VARCHAR(8), GETDATE(), 112); -- 20250105 SELECT CONVERT(VARCHAR(23), GETDATE(), 121); -- 2025-01-05 14:23:45.123我整理了几个最高频的样式码强烈建议保存下来样式码输出格式典型用途10101/05/2025美国格式老系统常见10305/01/2025英式日/月/年1112025/01/05斜杠分隔日期11220250105纯数字日期适合文件名、存储键1202025-01-05 14:23:45标准日志格式1212025-01-05 14:23:45.123带毫秒的完整时间232025-01-05ISO日期202025-01-05 14:23:45与120等价我在做数据接口的时候最喜欢用112生成日期后缀给文件命名比如“订单_20250105.xlsx”排序和识别都非常直观。写日志、做报表展示时用120或121因为可读性最好。一个高频坑有人写CONVERT(VARCHAR(3), GETDATE(), 112)以为通过指定VARCHAR长度就能控制输出位数。实际上样式码112输出8位字符声明VARCHAR(3)只会把结果截断成“202”既不是年份也不是月份纯粹是截断后的垃圾值。年份截取应该用DATEPART或者RIGHT(CONVERT(VARCHAR(8), GETDATE(), 112), 4)之类的组合而不是靠缩短VARCHAR长度。2.3 DATEADD、DATEDIFF、DATEPART日期运算三兄弟日期运算在SQL Server里基本是三兄弟的天下DATEADD、DATEDIFF、DATEPART。DATEADD给日期加或减指定的时间间隔SELECT DATEADD(DAY, 30, GETDATE()); -- 30天后 SELECT DATEADD(MONTH, -3, GETDATE()); -- 3个月前 SELECT DATEADD(YEAR, 1, GETDATE()); -- 一年后DATEDIFF计算两个日期之间的差值SELECT DATEDIFF(DAY, 2025-01-01, 2025-01-31); -- 30 SELECT DATEDIFF(MONTH, 2024-01-01, 2025-01-01); -- 12DATEPART提取日期的某个部分SELECT DATEPART(YEAR, GETDATE()); -- 年 SELECT DATEPART(MONTH, GETDATE()); -- 月这组函数在报表统计里特别常用。比如按月统计销售额最简洁的写法之一就是SELECT DATEPART(YEAR, OrderDate) AS 年份, DATEPART(MONTH, OrderDate) AS 月份, SUM(Amount) AS 销售总额 FROM Orders GROUP BY DATEPART(YEAR, OrderDate), DATEPART(MONTH, OrderDate);注意DATEPART(WEEKDAY)的返回值跟数据库的DATEFIRST设置有关同样是星期一在不同设置下可能返回1也可能返回2。如果你要在脚本里判断“今天是不是周一”建议用DATENAME(WEEKDAY, GETDATE())取星期名称再和固定字符串比较。虽然性能略低但结果稳定、可读性更强谁看谁知道。2.4 FORMAT方便的格式化利器但生产环境要慎用SQL Server 2012开始提供FORMAT函数它可以使用.NET的格式字符串来格式化日期SELECT FORMAT(GETDATE(), yyyy-MM-dd); -- 2025-01-05 SELECT FORMAT(GETDATE(), yyyy年MM月dd日); -- 2025年01月05日 SELECT FORMAT(GETDATE(), yyyy-MM-dd HH:mm:ss);好用吗非常好用尤其面对中文日期、自定义格式需求的时候比CONVERT的样式码灵活太多。但我要给你一句忠告生产环境不要滥用。FORMAT底层走的是.NET CLR执行速度比CONVERT慢得多。我自己做过粗略对比在大数据量查询里把100万行逐行做FORMAT耗时可能是CONVERT的几十倍。正确做法是能用CONVERT样式码解决的绝不上FORMAT只有样式码满足不了、又必须在SQL里生成特定格式字符串的时候才用而且尽量控制在数据量小或一次性转换的场景。FORMAT还有一个隐藏的坑它依赖当前会话的语言和区域设置。同样一行FORMAT(GETDATE(), MMMM)在中文环境输出“一月”在英文环境输出“January”。如果你的应用程序连接串没有显式指定区域不同客户端可能看到不同结果。要指定就带上第三个参数比如FORMAT(GETDATE(), MMMM, en-US)。3. 字符串函数文本处理的全套工具3.1 长度、截取、定位一网打尽字符串函数是日常用得最多的函数没有之一。先看定位和截取这两组。LEN()返回字符串长度注意它返回的是字符个数而不是字节数而且会忽略末尾空格。如果你需要精确到字节用DATALENGTH()。这个区别在处理中文、表情符号等Unicode字符时特别容易出问题。比如SELECT LEN(abc ); -- 3末尾空格被忽略 SELECT DATALENGTH(abc ); -- 6很多新手用LEN做数据校验发现长度“变短”了其实就是末尾空格被忽略导致的。CHARINDEX()定位子串出现的位置SELECT CHARINDEX(, userexample.com); -- 5CHARINDEX在定位不到时返回0这个特性经常用来做“是否包含”的判断。如果需要从右边开始找或者支持通配符就轮到PATINDEX()登场。PATINDEX支持用%和_做模糊匹配比如判断一个字符串是否包含数字SELECT PATINDEX(%[0-9]%, abc123); -- 4SUBSTRING()按位置截取子串SELECT SUBSTRING(SQL Server函数大全, 5, 6); -- ServerLEFT()和RIGHT()则分别从字符串一端截取SELECT LEFT(SQL Server, 3); -- SQL SELECT RIGHT(SQL Server, 6); -- Server实际业务里最常见的需求是“从邮箱里提取用户名”和“从身份证号里提取出生日期”。前者可以这样写SELECT LEFT(Email, CHARINDEX(, Email) - 1) FROM Users;后者可以这样写SELECT SUBSTRING(IDCard, 7, 8) -- 身份证第7位到第14位是出生日期 FROM Employees;3.2 替换、去空格、大小写数据清洗的基本功数据清洗是几乎所有SQL开发都躲不过的活儿。REPLACE()完成简单替换SELECT REPLACE(SQL-Server-教程, -, _); -- SQL_Server_教程LTRIM()、RTRIM()分别去掉左侧和右侧空格SQL Server 2017起引入的TRIM()一步到位去两侧空格SELECT TRIM( hello ); -- helloUPPER()、LOWER()做大小写转换常用于规范化比较。比如登录查询时希望不区分用户名大小写就可以统一转成大写再比对。这里我要专门说一个很多人都会犯的错误REPLACE并不会更新原表的数据它只是在查询结果里返回替换后的字符串。如果你想把清洗结果真正写回表必须额外加UPDATE。类似地TRIM、UPPER这些函数都是纯函数不修改源数据。搞清楚这一点你就不会在调试时四处找“数据去哪了”。另一个经典坑是去空格不完全。你肉眼看见的“空格”可能不是普通空格而是制表符、换行符或者全角空格。处理从Excel或网页导入的数据时字符串里可能夹杂着CHAR(9)制表符、CHAR(13)回车、CHAR(10)换行。需要组合使用SELECT REPLACE(REPLACE(REPLACE(col, CHAR(13), ), CHAR(10), ), CHAR(9), ) FROM SomeTable;这条组合替换是我做数据导入时常用的套路直接把不可见字符全部清掉之后再去做格式校验会省心很多。3.3 拼接与拆分CONCAT、STRING_AGG、STRING_SPLIT字符串拼接的经典写法是加号SELECT 姓名 Name FROM Users;但加号有个老毛病如果任何一边是NULL整个结果就变成NULL。很多新人查出来整列为空排查半天也找不到原因。SQL Server 2012推出了CONCAT()它会自动把NULL当空字符串处理SELECT CONCAT(姓名, Name) FROM Users; -- NULL不会让结果变NULLSQL Server 2017又加了CONCAT_WS()可以用第一个参数作分隔符把多个字段拼起来SELECT CONCAT_WS(-, 2025, 01, 05); -- 2025-01-05如果想把分组结果里的多行数据拼到一个字段就要用STRING_AGGSQL Server 2017引入SELECT Department, STRING_AGG(EmployeeName, 、) AS 员工列表 FROM Employees GROUP BY Department;这个函数极大简化了“一对多拼接”的需求。在它出现之前实现同样的效果只能靠FOR XML PATH绕来绕去写起来痛苦读起来也痛苦。反向的拆分也有现成的STRING_SPLIT()SQL Server 2016引入SELECT value FROM STRING_SPLIT(a,b,c, ,);它返回一个包含三行的结果集。注意STRING_SPLIT只支持单字符分隔符如果你要按多字符分隔符比如,,去拆就拆不了这是很多人遇到的第一道坎。另外STRING_SPLIT输出的value列是NVARCHAR类型实际使用时通常需要再CAST一下。3.4 字符串与数字的互转CAST、CONVERT与TRY_*字符串转数字也是高频操作。最直接的是CAST和CONVERTSELECT CAST(123.45 AS DECIMAL(10,2)); SELECT CONVERT(INT, 567);但这两个函数在遇到非数字字符串时会直接报错。比如12a3转INT一转换就抛“转换失败”的异常整个查询中断。这种情况在导入外部数据、清洗脏数据时极其常见。SQL Server 2012起提供了一组TRY_系列函数TRY_CAST、TRY_CONVERT、TRY_PARSE。转换失败时返回NULL而不是抛错SELECT TRY_CAST(12a3 AS INT); -- NULL不报错 SELECT TRY_CONVERT(DECIMAL(10,2), 12.34); -- 12.34利用这个特性可以快速定位脏数据SELECT 原始值 FROM ImportData WHERE TRY_CAST(原始值 AS INT) IS NULL;这个方法可以说是清洗数据时的首选武器。等确认完哪些数据有问题、修好之后再替换成普通CAST去正式入库效率会高很多。4. 数学函数数值计算的常用武器4.1 舍入三兄弟ROUND、CEILING、FLOOR数值计算里最容易被误会的是舍入。“四舍五入”是很多业务需求但SQL Server的ROUND支持第三个参数可以控制是四舍五入还是纯截断SELECT ROUND(3.14159, 2); -- 3.14 SELECT ROUND(3.14159, 2, 1); -- 第三个参数1时截断 SELECT ROUND(3.146, 2, 1); -- 3.14直接截断不进位第三参数为1时只保留指定位数不进位。这在某些财务场景反而更安全因为截断是确定性的不会因为边界值产生“是否该进位”的争议。CEILING()向上取整FLOOR()向下取整SELECT CEILING(4.1); -- 5 SELECT FLOOR(4.9); -- 4注意这两个函数对负数也遵循“向上”和“向下”的方向而不是简单的绝对值取整。CEILING(-4.1)返回-4因为-4比-4.1大FLOOR(-4.1)返回-5因为-5比-4.1小。新手很容易在这里栽跟头。还有一个容易忽略的细节ROUND返回的结果可能带末尾的0。比如ROUND(123.45, 1)结果是123.50末尾的0会保留。如果需要去掉末尾0通常要再配合字符串函数处理或者用FORMAT控制展示格式。4.2 常用计算函数ABS、POWER、SQRT、SIGNABS()取绝对值没什么好说的SELECT ABS(-8); -- 8POWER()做幂运算SELECT POWER(2, 10); -- 1024SQRT()取平方根SELECT SQRT(16); -- 4SIGN()返回数字的符号正数返回1、负数返回-1、0返回0。这个函数在做趋势判断时很实用比如判断两个月的销售额变化方向SELECT SIGN(本月销售额 - 上月销售额) AS 趋势 FROM Sales;数学函数单独使用都不难真正的难点在于和业务逻辑结合。比如计算复利、汇率换算、距离计算都是多个数学函数的组合。以汇率换算为例SELECT Amount * CONVERT(DECIMAL(10,4), Rate) FROM Transactions;这里需要注意数据类型的精度。DECIMAL(p,s)的设置很关键如果精度设置不当乘法结果可能被隐式转换造成精度丢失甚至结果变成科学计数法。我做财务类报表时对金额字段一律用DECIMAL(18,4)或DECIMAL(18,2)不用FLOAT。FLOAT是浮点数二进制表示方式在累加时会产生微小误差最终影响对账结果。这一点在金额计算上绝对是红线级别的禁忌。4.3 RAND随机数的正确打开方式RAND()生成0到1之间的随机小数SELECT RAND(); -- 0.735465...常见需求是生成指定范围的随机整数比如1到100之间的数公式是SELECT FLOOR(RAND() * 100) 1;但有一个细节经常被忽略如果没有指定种子RAND()每次调用都可能返回不同的值如果在同一批查询里多次调用还可能产生重复值。在高并发场景、或者需要生成唯一随机码时不要指望RAND更可靠的方式是用NEWID()作为随机源SELECT ABS(CHECKSUM(NEWID())) % 100 1;NEWID()生成GUID每次互不相同CHECKSUM把它映射成一个整数再取模得到范围内的数。这个方法在测试数据填充、随机抽样场景里很常用。不过无论是RAND还是NEWID方案都不能单独用来生成加密级随机数或者业务主键随机码那需要更严谨的机制。这类任务建议放到应用层处理而不是在SQL里硬啃。5. 聚合函数与分组统计实战5.1 五大基础聚合SUM、AVG、COUNT、MIN、MAX聚合函数是SQL查询里统计分析的支柱。最基础的是这五个SUM() 求和只能作用于数值类型AVG() 求平均值同样只能用于数值类型COUNT() 统计行数COUNT(*)统计所有行COUNT(列名)统计该列非NULL的行数MIN()、MAX() 求最小值和最大值可用于数值、日期、字符串一句话提醒COUNT(列名)和COUNT()是两回事。COUNT()包含NULL行的数量COUNT(列名)只计数非NULL值。如果你的列允许NULL统计结果可能显著不同。比如统计订单表里“有多少用户填写了备注”用COUNT(备注)是对的用COUNT(*)就会把没写的也算进去。AVG也一样它在计算时会排除NULL值。如果你业务上希望把NULL当作0参与平均需要提前处理SELECT AVG(ISNULL(Score, 0)) FROM StudentScores;这个需求在做绩效统计时经常遇到。但要注意处理方式会影响结果的业务含义得先和需求方确认清楚是“有成绩的人的平均分”还是“所有人的平均分没成绩算0”。5.2 GROUP BY与HAVING分组过滤的逻辑聚合函数单独用比较简单一旦配合GROUP BY“维度指标”的报表结构就出来了SELECT 城市, COUNT(*) AS 客户数, SUM(消费金额) AS 总消费额 FROM 客户表 GROUP BY 城市;GROUP BY之后查询结果里只能出现分组列和聚合函数结果其他列都不能直接select出来。这是新手最常见的报错来源“列xxx在select列表中无效因为它既不包含在聚合函数中也不包含在GROUP BY子句中”。遇到这个报错先检查select列表里的非聚合列有没有都放进GROUP BY。HAVING是专门针对分组后的条件过滤用的它跟WHERE最大的区别是WHERE在分组之前执行不能用聚合函数HAVING在分组之后执行可以用聚合函数SELECT 城市, SUM(消费金额) AS 总消费额 FROM 客户表 GROUP BY 城市 HAVING SUM(消费金额) 10000;想过滤“总消费额大于1万的城市”你绝对不能用WHERE SUM(消费金额) 10000那会直接报错。逻辑顺序是先把数据按城市分组计算完聚合结果之后再做HAVING条件筛选。5.3 高级分组GROUPING SETS、ROLLUP、CUBE基础分组之外SQL Server还提供了一些“高级分组”的扩展能力做报表时非常好用。ROLLUP生成小计和总计SELECT 年份, 季度, SUM(销售额) FROM 销售表 GROUP BY ROLLUP(年份, 季度);得到的每一组层级组合都会有对应的汇总行最后还有一行全表总计。做分层报表时特别实用。CUBE则按所选列的所有可能组合都生成汇总行输出的组合数会指数级增长适合多维度交叉统计但要小心结果集膨胀。数据量一大CUBE的输出行数可能会远超预期。GROUPING SETS最灵活可以精确指定需要哪些维度的汇总SELECT 年份, 季度, SUM(销售额) FROM 销售表 GROUP BY GROUPING SETS ((年份, 季度), (年份), ());最后一个空括号()表示总计行。这三者的区别我在实际项目里这样总结ROLLUP适合有明显层级关系的维度比如年-月-日CUBE适合平级多维度交叉GROUPING SETS适合你已经知道要哪些组合、不想多算的情况。数据量大时用GROUPING SETS还能省下不少计算资源。5.4 窗口函数聚合的进阶形态除了GROUP BYSQL Server从2012年起支持完整的窗口函数体系也就是OVER()。它能在不减少行数的情况下计算聚合值非常适合做累计、排名、移动平均SELECT 姓名, 销售额, SUM(销售额) OVER (ORDER BY 月份) AS 累计销售额 FROM 销售表;再比如ROW_NUMBER()做分组排名SELECT 姓名, 部门, 销售额, ROW_NUMBER() OVER (PARTITION BY 部门 ORDER BY 销售额 DESC) AS 排名 FROM 销售表;这个写法就是经典的“按部门内销售额排名”。窗口函数是聚合函数的天生搭档强烈建议所有做报表的人都熟练掌握。它和GROUP BY不是替代关系而是互补关系GROUP BY压缩行数OVER保留行数同时附加聚合信息。两者各有适用场景用对地方效率才会高。6. 常见问题与排查技巧实录6.1 日期转换失败的典型场景遇到“从字符串转换日期和/或时间字符串时转换失败”这个报错先不要慌。90%的情况是字符串里的日期格式跟当前会话的日期格式不匹配。默认语言是英语us_english时SQL Server对01/05/2025的理解可能和你的预期完全相反。最稳妥的解决方案是用无歧义的格式20250105或者2025-01-05T00:00:00。这种格式不受会话语言的影响谁解析结果都一样。另外日期字符串里藏了看不见的字符也很常见。数据从Excel复制过来可能有全角空格、不可见字符。排查时先把字符串的长度和ASCII值打出来看看SELECT LEN(日期字符串), ASCII(SUBSTRING(日期字符串, 1, 1)), ASCII(SUBSTRING(日期字符串, 2, 1)) FROM 问题表;ASCII值一旦不符合预期你就知道里面混进了什么样的特殊字符。这种问题肉眼根本看不出来必须用函数拆解。6.2 字符串函数使用时的常见坑字符串的坑集中在这三处长度单位、NULL传播、隐式类型转换。长度单位的问题前面说过LEN返回字符数DATALENGTH返回字节数处理中文时尤其明显。一个中文字符在UTF-8编码下占3个字节在NVARCHAR下占2个字节混在一起统计必然出错。NULL传播的问题就是加号拼接时遇到NULL就整体变NULL解决办法是ISNULL包裹或者改用CONCAT。隐式类型转换的坑则体现在性能上。当你在WHERE子句里对一个索引列做函数处理时比如WHERE LEFT(Code, 3) ABCSQL Server就没法正常走索引了因为索引键被函数改变了。正确的写法是用Code LIKE ABC%这样既能利用索引语义上也完全一样。这是一个性能差异可能在百倍级别的细节值得记一辈子。6.3 聚合结果对不上的排查思路如果你发现GROUP BY统计的结果跟业务口径对不上先检查三件事NULL是否被正确处理、是否有重复数据、维度组合是否有遗漏。比如COUNT(列名)遇到的NULL不算数、SUM遇到NULL自动跳过但平均值却可能因为NULL数量而变化。重复数据可以通过COUNT(*)对比COUNT(DISTINCT 主键)来快速发现。维度遗漏则往往出现在多表JOIN时内连接把没有匹配上的数据直接过滤掉了统计结果自然和业务预期差一大截。再一个高频场景查询用了DISTINCT或TOP又同时用了GROUP BY统计口径会变得非常绕。遇到“数量多了/少了”的情况建议把语句拆开分步验证每一步的行数变化缩小问题范围。这是排查SQL问题的基本方法论比盯着整条语句空想高效得多。6.4 性能相关的细节提醒最后说几个性能细节。聚合统计时尽量不要对索引列套函数再分组。前面提到的LIKE替代LEFT就是典型例子。同理DATEADD和DATEDIFF在条件里使用也可能阻断索引能改写成范围条件就尽量改写。字段类型的选择也影响性能。纯中文或字母数字内容如果不需要存Unicode特殊字符VARCHAR比NVARCHAR占用空间更小读写性能更好。但如果系统未来可能扩展到多语言场景还是建议用NVARCHAR避免后期编码转换踩坑。还有一点很实用字符串拼接场景下的批量更新要避免在循环里逐行UPDATE。能一次性批量完成的尽量使用临时表加JOIN的方式或者用CASE WHEN批量赋值。循环逐行更新在大数据量下慢到怀疑人生。我亲眼见过同事用WHILE循环更新30万行跑了将近一小时改成基于JOIN的整批更新后十几秒就完成了。这个对比就是“不懂性能细节”和“懂性能细节”之间的真实差距。写到这里四大类函数基本都过了一遍。我个人在实际操作中的体会是函数这东西背是背不完的也不需要背完。真正重要的是建立“遇到问题知道该查哪一类函数”的敏感度然后把最常用的那20个练到肌肉记忆剩下的用到时再查完全来得及。我自己的一个小习惯是每过一段时间就把近期写的SQL翻出来复盘一遍看看哪些地方还能用更好的函数替代比如把FOR XML PATH换成STRING_AGG、把逐行循环改成窗口函数。这个习惯坚持了几年写查询的速度和代码的可读性都有了明显提升。希望这份整理也能成为你日常写SQL时的参考手册踩过的坑别再踩写出来的代码一次跑通。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →