十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SQL Server模糊查询与常用查询函数全解析:LIKE通配符、索引优化及函数实战

SQL Server模糊查询与常用查询函数全解析:LIKE通配符、索引优化及函数实战 写这篇文章的起因是前两天一位刚转岗做数据报表的朋友问我SQL Server里想查某个客户名字里带“华”字的所有记录为什么LIKE %华%有时候查得出来有时候查不出来还有时候慢得要命这个问题看着基础真要讲清楚其实能扯出一大串通配符的写法、大小写排序规则、索引能不能命中、函数搭配怎么用、NULL值陷阱、转义字符……所以我干脆把SQL Server里模糊查询和常用查询函数这块儿完整梳理一遍既是给新手一份能“抄作业”的实操手册也算给我自己做个备忘。这篇文章没有任何版本歧视SQL Server 2008 R2到2022我都用过文里的SQL语法在主流版本上基本通用遇到个别版本差异我会单独标注。内容涵盖LIKE通配符的完整用法、模糊查询的性能优化思路、字符串/聚合/日期三大类查询函数以及我这些年实际踩过的坑。适合刚入门SQL的在校生、天天写报表的数据分析师以及正在维护老系统的开发同学。1. 模糊查询的核心LIKE 用法拆解1.1 四种通配符先把这个记死LIKE的核心价值就四个字模糊匹配。它靠通配符去匹配“包含关系”“开头关系”“位置关系”比等号那种“非黑即白”的死匹配灵活太多。我做培训的时候喜欢打一个比方等号匹配就像找一栋楼必须门牌号完全一样LIKE匹配则像只告诉快递员“小区名里有‘江’字”他就能找出一圈候选地址。SQL Server里的LIKE通配符一共四种通配符作用示例匹配结果%匹配任意长度的字符包括0个字符WHERE name LIKE 张%张三、张伟、张_匹配单个任意字符WHERE name LIKE 张_张三、张飞仅两个字的[]匹配指定范围内的单个字符WHERE name LIKE [张李王]三张三、李三、王三[^]匹配不在指定范围内的单个字符WHERE name LIKE [^张李王]三赵三、刘三排除张/李/王初学阶段最容易混的是_和%。_严格占一个字符位你写LIKE 张_就绝对匹配不到“张伟强”因为伟后面还有“强”长度不匹配。%则不然它代表的是零到无数个字符LIKE 张%既能匹配“张”也能匹配“张三”、“张伟强”、“张无忌的大舅子”。方括号[]的使用频率没那么高但某些场景特别好用。比如你要查所有名字里带数字的客户可以直接WHERE 客户名 LIKE %[0-9]%一条语句把所有包含阿拉伯数字的名称全捞出来这个比写一堆OR条件干净得多。字母范围也同理LIKE [A-Z]%能匹配所有以大写字母开头的数据。1.2 大小写敏感与排序规则查不出来多半是这个原因我见过太多人写LIKE %abc%却发现大写ABC的记录一条也查不出来第一反应是数据有问题其实九成情况是排序规则Collation在作祟。SQL Server的排序规则分两大类_CI_表示大小写不敏感Case Insensitive_CS_表示大小写敏感Case Sensitive。比如安装时默认常见的Chinese_PRC_CI_AS和SQL_Latin1_General_CP1_CI_AS都是CI不区分大小写所以你的LIKE查询里大小写都能匹配上。但如果数据库或者字段级别把排序规则设置成了Latin1_General_CS_AS那LIKE %abc%就只会匹配小写abcABC一条都出不来。你可以在查询里这样确认-- 查看当前数据库的排序规则 SELECT DATABASEPROPERTYEX(DB_NAME(), Collation) AS DatabaseCollation; -- 直接在查询时临时指定大小写不敏感推荐方式 SELECT * FROM 用户表 WHERE 用户名 COLLATE Latin1_General_CI_AS LIKE %abc%;这个坑在接口对接场景尤其常见业务系统表是CS排序规则前端搜索框传过来的是小写关键字两边一碰就查漏数据。我的建议是不要在LIKE的查询条件里过度依赖默认排序规则拿不准的时候直接显式指定COLLATE一次把规则定死别让环境差异背锅。1.3 转义字符让通配符变成普通字符继续讨论数据里真的包含%或_的情况。比如产品编码规则是“前缀%后缀”格式你要查编码里含%的记录直接写LIKE %%%肯定乱套——三个%到底哪个是通配符哪个是普通字符SQL Server分不清。解决办法是转义。SQL Server用ESCAPE关键字指定转义字符推荐用感叹号!或反斜杠\不推荐用方括号语法因为可读性太差-- 查编码中包含“%”字符的记录 SELECT * FROM 产品表 WHERE 产品编码 LIKE %!%% ESCAPE !; -- 查编码中包含“_”下划线的记录 SELECT * FROM 产品表 WHERE 产品编码 LIKE %!_% ESCAPE !;ESCAPE的实际含义就是告诉SQL Server感叹号后面的那个字符请当作普通字符处理不要当通配符。这里的ESCAPE !语法在SQL Server 2005以后的版本都支持老项目也完全能用不存在兼容性问题。还有一种老式语法是用方括号把通配符包起来LIKE %[%]%也能匹配到包含%的记录。这种写法在PostgreSQL、MySQL里也通用但我个人还是更推荐ESCAPE因为方括号语法一旦和字符集范围混在一起很容易写出LIKE %[[]%这种自己都看不懂的表达式。1.4 LIKE 前端搜索框的最常见拼接写法在正式开发一个带搜索框的页面时常用的做法是后端收到关键字参数后把它拼进LIKE条件。这里我最想提醒的是不要在EF Core、Dapper或者存储过程里用字符串拼接的方式直接怼参数而是优先用参数化查询。比如C#加Dapper的经典写法var keyword % input.Trim() %; var list connection.QueryUser( SELECT * FROM UserInfo WHERE UserName LIKE keyword, new { keyword }).ToList();注意输入过滤不只是为了防注入LIKE条件还有个隐藏问题如果用户输入的内容里带了%、_、[这些通配符你的搜索会被带偏。比如用户搜“A%”你拼成LIKE %A%%他会把所有包含“A”的记录全查出来。所以更严谨的做法是先把用户输入里的通配符转义掉再拼通配input input.Replace([, [[]).Replace(%, [%]).Replace(_, [_]); var keyword % input %;这样用户搜“A%”的时候%被当作普通文本处理查出来的就是真正包含“A%”字样的记录而不是把全表以A开头的都算上。我把这个处理逻辑封装在工具方法里每个新项目都是直接复制过去。2. 模糊查询性能调优LIKE会不会走索引2.1 前缀匹配走索引中间匹配全表扫先给结论LIKE abc%这种前缀匹配通配符在最后在SQL Server里是可以使用索引的LIKE %abc%这种中间匹配通配符在最前索引就帮不上忙了只能全表扫描。原理并不难理解索引是按照B树的顺序排列的类似于字典按拼音排好序。你查“以abc开头”的数据等同于在字典里翻到“abc”的区间效率极高。但你要查“所有包含abc的数据”就像要求你在字典里找出所有正文里出现过“abc”这三个字母的条目——单靠目录排序做不到只能一页一页翻。看一个实际例子。假设订单表的订单号列上建了索引下面两种写法性能差距巨大-- 能高效走索引百万级数据毫秒级返回 SELECT * FROM 订单表 WHERE 订单号 LIKE SO2024%; -- 索引失效全表扫描数据量大时直接卡死 SELECT * FROM 订单表 WHERE 订单号 LIKE %SO2024%;SEO时代大家喜欢把搜索框做成含任意位置的模糊匹配但对大表来说这是性能毒药。如果业务上确实需要从任意位置匹配有几个替代方案第一数据量小十万行以内且并发低直接全表扫其实没多大事第二数据量大可以引入全文索引Full-Text Index用CONTAINS代替LIKE第三如果只是固定几个前缀规则加冗余字段存前缀用前缀匹配。2.2 CHARINDEX 与 PATINDEX模糊匹配的替代函数LIKE是条件匹配但有时候你需要在SELECT结果里直接返回“关键字出现的位置”那就要用到CHARINDEX和PATINDEX了恰好在“查询函数”范畴里。CHARINDEX用于查找一个字符串在另一个字符串中的起始位置找不到就返回0PATINDEX则支持用通配符去查找模式串的起始位置是“函数版LIKE”。-- 返回3因为“sql”从第3个字符开始 SELECT CHARINDEX(sql, my sql server); -- 返回7第7-9位是“123”符合[0-9][0-9][0-9]格式 SELECT PATINDEX(%[0-9][0-9][0-9]%, abc123def); -- 用CHARINDEX判断包含关系等价于LIKE SELECT * FROM 产品表 WHERE CHARINDEX(华为, 产品名称) 0;这里有个性能相关的经验WHERE CHARINDEX(关键字, 字段) 0的写法也是无法走索引的和LIKE %关键字%是难兄难弟。但从写法上看CHARINDEX适合在关键字本身也是动态变量、需要进一步参与计算的场景。比如你要“找出商品描述里第二次出现‘优惠’的位置”用LIKE很难优雅实现CHARINDEX三参数版本直接解决-- 第三个参数2表示从第2位开始找 SELECT CHARINDEX(优惠, 商品描述, 2) AS 第二次出现位置 FROM 商品表;2.3 全文索引大文本模糊搜索的终极方案附适用条件如果某张表的body字段存的是长篇文章你还要按文章内容做搜索LIKE %关键词%在大数据量下基本跑不动。这时候SQL Server的全文索引节点就非常值得研究。全文索引的基本使用套路是先建索引再用CONTAINS或FREETEXT查询。-- 创建全文索引需要先有一个唯一索引 CREATE FULLTEXT CATALOG ft_catalog AS DEFAULT; CREATE FULLTEXT INDEX ON 文章表(正文) KEY INDEX PK_文章表 WITH STOPLIST SYSTEM; -- 查询正文中包含“数据库优化”的记录 SELECT * FROM 文章表 WHERE CONTAINS(正文, 数据库优化); -- 按词形变化匹配能匹配到“running”等变形 SELECT * FROM 文章表 WHERE FREETEXT(正文, run);我的实际体感全文索引对中文分词的支持没有云搜索那么聪明但对付固定词组查询完全够用。注意全文索引的对象是词而不是字符所以CONTAINS(正文, 数据)查不到“数据库优化”里单独的“数据”除非启用中文分词特性。如果你的需求只是字段前缀匹配比如邮编、订单号那全文索引是杀鸡用牛刀老老实实用LIKE 前缀%就好。3. 查询函数的第一梯队字符串函数全解析3.1 截取类LEFT、RIGHT、SUBSTRING做数据清洗时字符串截取是三板斧。比如订单号SO2024123456想取年份和流水号用函数直接拆SELECT 订单号, LEFT(订单号, 2) AS 前缀, SUBSTRING(订单号, 3, 4) AS 年份, RIGHT(订单号, 6) AS 流水号 FROM 订单表;LEFT(字符串, 长度)从左边截取指定长度RIGHT(字符串, 长度)从右边截取指定长度SUBSTRING(字符串, 起始位置, 长度)从任意位置截取指定长度起始位置从1开始计数这里最值得提醒的是SUBSTRING的边界问题。很多人把起始位置和长度搞混尤其是从1开始还是从0开始SQL Server是从1开始SUBSTRING(abcd, 1, 2)返回的是ab而不是abc。我做报表时曾经因为从0开始取导致所有编码第一位被吞掉排查半天才反应过来。另外SUBSTRING配合CHARINDEX可以轻松提取两个分隔符之间的内容比如从“品牌-型号-容量”中提取型号SELECT SUBSTRING( 商品全名, CHARINDEX(-, 商品全名) 1, CHARINDEX(-, 商品全名, CHARINDEX(-, 商品全名) 1) - CHARINDEX(-, 商品全名) - 1 ) AS 型号 FROM 商品表;这段代码看着绕本质就是先找到第一个-的位置再找到第二个-的位置两者中间的长度就是型号部分。刚接触会觉得嵌套很深但这是字符串解析的经典模式用熟了顺手得很。3.2 查找定位类LEN、CHARINDEX、PATINDEX前面提到过CHARINDEX和PATINDEX这里和LEN、DATALENGTH放到一起看。LEN返回字符串的字符数但要注意它不计算尾随空格。LEN(abc )返回3而不是5。这经常成为数据验证里的盲区你看着字符串有空格LEN却告诉你没空格。如果需要精确的字节数尤其是存储中文字符一个汉字占2字节的场景就得用DATALENGTH-- LEN不数尾随空格DATALENGTH数实际字节 SELECT LEN(abc ) AS len_val, -- 3 DATALENGTH(abc ) AS datalen_val, -- 6含3个空格 DATALENGTH(数据库) AS chinese_byte; -- 6一个汉字2字节PATINDEX是模糊匹配的函数版它对大小写是否敏感同样遵循所在数据库的排序规则。用它判断一段文本是否符合特定字符模式比LIKE更有优势因为它能返回值而不是布尔逻辑比如判断手机号是否纯数字开头SELECT 手机号, PATINDEX(%[^0-9]%, 手机号) AS 首个非数字位置 FROM 用户表;如果查询结果全是0说明手机号全部由数字组成如果返回某个正整数说明该位置出现了非数字字符。这种“数据质量体检”写法比嵌套一堆REPLACE高效太多。3.3 替换改造类REPLACE、STUFF、LTRIM/RTRIM清洗脏数据时REPLACE出镜率最高比如把历史录入错误的全角逗号统一改成半角、把电话号码里的横杠去掉SELECT REPLACE(电话号码, -, ) AS 去横杠号码, REPLACE(REPLACE(地址, , ,), 。, .) AS 规范地址 FROM 客户表;STUFF是个更隐蔽但超有用的字符串缝合函数。它的语法是STUFF(原字符串, 起始位置, 删除长度, 插入字符串)作用是把指定位置的内容替换掉并且插入新内容。最经典的场景是手机号脱敏或者银行卡中四位打星-- 把手机号第4位开始的4位替换成**** SELECT STUFF(手机号, 4, 4, ****) AS 脱敏手机号 FROM 用户表; -- 13812345678 - 138****5678LTRIM和RTRIM分别去掉左边和右边的空格。SQL Server 2017以后出了个TRIM函数可以同时去两边空格而且还支持指定字符但考虑到还有大量2016及以前的存量系统我建议写脚本时还是老老实实用LTRIM(RTRIM(字段))组合兼容性最稳。再补充一下大小写转换LOWER和UPPER适合在邮箱匹配和一些业务代码归一化的场景。注意它们对中文没有影响因为中文没有大小写概念。SELECT UPPER(abcqq.com); -- ABCQQ.COM3.4 拼接类加号 与 CONCAT 的区别字符串拼接这块儿有个经典大坑SQL Server里用拼接遇到NULL就整体变成NULL。比如客户表里姓氏和名字分开存储其中一个为NULL你SELECT 姓 名 AS 全名查出来就是NULL应用层展示直接空一列。CONCAT函数SQL Server 2012则不同它会自动把NULL当成空字符串处理-- 老写法只要有一个NULL全名为NULL SELECT 姓 名 AS 全名 FROM 客户表; -- 新写法NULL自动忽略拼接结果更符预期 SELECT CONCAT(姓, 名) AS 全名 FROM 客户表;我这里想强调两个实操建议。其一如果你的服务器版本是2012以上直接默认用CONCAT少一个NULL坑就少一次线上事故其二如果项目还在2008 R2那只能用ISNULL先把NULL转成空串SELECT ISNULL(姓, ) ISNULL(名, ) AS 全名 FROM 客户表;另外拼接数字时会先把数字隐式转换成字符串但需要注意顺序。SELECT 1 2 3在不同数据库里可能会得到33字符串33或6整数6SQL Server遵循表达式从左到右的类型转换规则这里也容易出隐性bug。我建议混合拼接时一律先转成VARCHARSELECT CAST(订单数量 AS VARCHAR(10)) 件 FROM 订单表;4. 查询函数的第二梯队聚合函数与分组统计实战4.1 五大聚合函数SUM、AVG、COUNT、MAX、MIN聚合函数的价值是把多行数据浓缩成一行统计结果。SQL Server中最常用的五个SUM(字段)求和只适用于数值类型AVG(字段)求平均值自动忽略NULLCOUNT(字段)计数COUNT(*)统计所有行COUNT(列名)统计该列非NULL的行数MAX(字段)/MIN(字段)求最大/最小值适用于数值、字符串、日期我特别想强调COUNT(*)和COUNT(列)的差别。举例客户表有100条记录其中手机号字段有3条是空值那么COUNT(*)返回100COUNT(手机号)返回97。这个差异在写报表时非常容易踩坑——你想统计“有多少客户填了手机号”结果写了个COUNT(*)把没填手机号的也一起算进去了数据直接虚高。另外一个隐蔽点是AVG自动忽略NULL这会导致平均值的“分母”变小。比如3笔订单金额分别是100、200、NULLAVG(金额)结果是150而不是100。理由也合理NULL代表未知不该参与计算。但业务上你可能希望NULL按0参与分母计算这时得用AVG(ISNULL(金额, 0))把NULL先转换成0。4.2 GROUP BY 分组统计的黄金搭配聚合函数不配GROUP BY就只能输出一行总计真正日常报表大多需要按维度分组比如“每个月的销售总额”“每个部门的平均工资”。SELECT 部门ID, COUNT(*) AS 员工数, AVG(工资) AS 平均工资, MAX(工资) AS 最高工资, MIN(工资) AS 最低工资, SUM(工资) AS 工资总和 FROM 员工表 GROUP BY 部门ID;GROUP BY的语法约束是SELECT子句里出现的列要么出现在GROUP BY里要么被聚合函数包裹否则SQL Server直接报错。这条规则看似死板其实是帮你守住逻辑边界——你想统计部门维度就不能在明细里露员工姓名否则语义说不通。还有个频率极高的坑GROUP BY之后想过滤分组结果用WHERE是无效的。WHERE在分组之前执行只能过滤原始行分组之后的条件要用HAVING-- 找出订单数超过100个的客户 SELECT 客户ID, COUNT(*) AS 订单数 FROM 订单表 GROUP BY 客户ID HAVING COUNT(*) 100;如果同时有WHERE和HAVING执行顺序是WHERE先过滤原始行然后分组再做HAVING过滤分组结果最后SELECT输出。理解这个顺序写复杂统计语句基本不会乱。4.3 一个完整的多函数组合案例现在串一个贴近业务的例子统计每个产品分类下价格超过100元的产品数量以及其中最便宜的价格。这个需求既要用WHERE过滤明细又要GROUP BY分组还要用聚合函数统计SELECT 分类ID, COUNT(*) AS 高价产品数, MIN(单价) AS 最低价, MAX(单价) AS 最高价, ROUND(AVG(单价), 2) AS 平均价 FROM 产品表 WHERE 单价 100 GROUP BY 分类ID ORDER BY 高价产品数 DESC;这里ROUND(AVG(单价), 2)是嵌套函数——先用AVG求平均值再用ROUND保留两位小数防止原始平均价出现一长串小数。SQL的嵌套函数很常见运算顺序是从内向外和数学公式一样。这种嵌套逻辑你写得越多读别人的复杂查询就越轻松。5. 时间与转换函数查询函数里最容易被忽略的角落5.1 日期函数GETDATE、DATEADD、DATEDIFF业务查询里日期过滤是家常便饭但很多人写日期条件时特别喜欢WHERE 下单时间 2024-01-01然后发现白天下的单一条都查不出来因为下单时间是datetime类型精确到了时分秒而’2024-01-01‘是零点时刻两者对不上。更稳的写法是用范围条件-- 推荐左闭右开区间 SELECT * FROM 订单表 WHERE 下单时间 2024-01-01 AND 下单时间 2024-01-02; -- 或者用CONVERT去掉时间部分再比较 SELECT * FROM 订单表 WHERE CONVERT(date, 下单时间) 2024-01-01;用函数包住字段如CONVERT(date, 下单时间)会让索引失效大数据量下不推荐所以首选还是范围条件法。动态日期计算需要用到DATEADD和DATEDIFF。DATEADD给日期增加或减去一个时间间隔DATEDIFF计算两个日期的间隔数-- 查询最近7天的订单 SELECT * FROM 订单表 WHERE 下单时间 DATEADD(day, -7, GETDATE()); -- 计算客户注册至今的天数 SELECT 客户名, DATEDIFF(day, 注册日期, GETDATE()) AS 注册天数 FROM 客户表; -- 查询本周的订单本周从周一开始取决于DATEFIRST SELECT * FROM 订单表 WHERE 下单时间 DATEADD(day, 1 - DATEPART(weekday, GETDATE()), CAST(GETDATE() AS date));这里DATEPART(weekday, GETDATE())返回今天是本周第几天比如周日返回1默认设置下、周一直返2。用它反推本周起始日的写法是SQL Server里标准的周报统计套路值得直接抄走。5.2 格式转换三件套CAST、CONVERT、FORMAT数据导入导出时字符串和日期、数字之间互相转换是高频操作。CAST是标准SQL语法CONVERT是SQL Server扩展支持更多样式码FORMAT则是2012以后才有的花活灵活但性能差。-- CAST 基础用法 SELECT CAST(下单时间 AS date) AS 下单日期 FROM 订单表; -- CONVERT 转字符串并指定样式 112yyyymmdd SELECT CONVERT(varchar(10), 下单时间, 112) AS 日期键 FROM 订单表; -- FORMAT 自定义格式中文环境下好用但慢 SELECT FORMAT(下单时间, yyyy-MM-dd) AS 日期字符串 FROM 订单表;我的建议是数字和短日期转换用CONVERT因为它有丰富的样式码跨数据库移植性要求高时用CAST只有需要非常规格式比如“2024年01月”这种中文日月格式才用FORMAT而且数据量大时必须谨慎——FORMAT是基于.NET CLR实现的性能比CONVERT差一到两个数量级我在百万级数据上实测过慢得让人怀疑人生。5.3 时间函数与模糊查询的联动场景字符串函数和日期函数经常和LIKE绑在一起用。比如很多老系统会把日期存成YYYYMMDD格式的字符串你要按月份模糊查询直接LIKE 202401%就能命中所有1月的数据-- 月份维度统计日期存储为字符串 yyyyMMdd SELECT LEFT(订单日期, 6) AS 年月, COUNT(*) AS 订单数 FROM 订单流水表 WHERE 订单日期 LIKE 2024% GROUP BY LEFT(订单日期, 6);这种用LIKE处理定长字符串日期的写法能避开各种日期格式转换带来的坑。但提醒一句它只适用于“存储格式严格统一”的表。如果这个字符串列偶尔出现2024-01-05或者2024015这种脏数据LIKE查询就会漏计或者错计前置的数据质量检查跑不掉。6. 常见问题与排查技巧实录6.1 模糊查询慢到爆炸怎么查原因先看执行计划。SQL Server Management Studio里按CtrlM开启“包含实际执行计划”再执行查询看有没有表扫描Table Scan或聚集索引扫描Clustered Index Scan以及扫描预估行数是不是几十万起步。如果确认是LIKE %关键字%导致的全表扫描按数据量分三个档处理两万行以内别折腾直接扫成本可忽略两万到两百万行考虑改造成前缀LIKE或者用冗余字段缓存一份可前缀匹配的数据超过两百万行且搜索并发高直接上全文索引或者迁到专门的搜索引擎组件不要在SQL Server里硬扛。还有个人为制造的慢查询在LIKE关键字两端加%之前应用层忘了Trim导致传入的条件里带着前后空格LIKE % 关键字 %自然匹配不到预期结果但执行的代价却是全表扫。处理方式很简单抓SQL日志看实际执行语句即可快速定位。6.2 明明数据存在LIKE却查不到排查方向清单这种问题基本可以按顺序排查第一看是否有不可见字符比如全角空格、回车换行用DATALENGTH对比字段长度或者SELECT [ 字段 ]把字符串前后边界标出来第二看排序规则按文章开头提到的方法检查CS/CI必要时临时加COLLATE验证第三看字段是否真的是字符串类型有时候数值类型字段被隐式转换LIKE %123%匹配0结果改用CAST(字段 AS VARCHAR)再查第四确认不是NULL因为LIKE碰上NULL既不算匹配也不算不匹配直接返回UNKNOWN结果就是查不到这种场景用IS NULL或ISNULL处理。NULL这个坑我多说一句WHERE 字段 NOT LIKE %xxx%在字段值NULL时也返回UNKNOWN也就是说这一行既不会被NOT LIKE条件保留下来。如果你期望“非匹配”的集合里包含NULL行得显式加OR 字段 IS NULLSELECT * FROM 客户表 WHERE 备注 NOT LIKE %黑名单% OR 备注 IS NULL;6.3 如何快速验证一个LIKE查询是否会走索引临时用一个低代价的实验最快找一张50万行以上的表分别执行带前缀匹配和中间匹配的语句开启SET STATISTICS IO ON观察逻辑读取次数。前缀匹配的LIKE ABC%可能只有几十次逻辑读中间匹配的LIKE %ABC%往往直接几千次起步这个数字差就是性能和代价的最直观体现。SET STATISTICS IO ON; SELECT * FROM 订单表 WHERE 订单号 LIKE SO2024%; -- 逻辑读较少 SELECT * FROM 订单表 WHERE 订单号 LIKE %SO2024%; -- 逻辑读暴涨如果前缀LIKE也走不了索引大概率是字段上根本没建索引或者查询字段上套了函数比如WHERE LEFT(订单号, 2) SO后者属于典型的索引杀手改成LIKE SO%才能救回来。6.4 排序规则冲突多表关联LIKE报错两个表关联后做模糊查询如果两边字段的排序规则不同比如一个是Chinese_PRC_CI_AS一个是SQL_Latin1_General_CP1_CI_ASSQL Server会直接报“无法解决排序规则冲突”的错误。解决办法是给其中一个字段显式指定COLLATESELECT * FROM 表A a JOIN 表B b ON a.名称 COLLATE Chinese_PRC_CI_AS b.名称 WHERE a.名称 LIKE %测试%;这种冲突常见于把老库和新库的表做跨库关联的场景。为了不动底表结构查询时手动调整字段的COLLATE是最安全和最小的改动方案。最后再分享一个配套的小技巧我在实际项目里还发现一个高频需求同时查多个关键词且关键词之间是“且”的关系。直接用AND嵌套LIKE没问题但逻辑一多就乱。我会先在应用层把关键词拆成数组然后拼多个LIKE条件每个条件都加上转义处理前文已讲。如果是“或”的关系我习惯用OR或者借助临时表存关键词再JOIN去判断-- 查询名称同时包含“旗舰”和“2024”的型号 SELECT * FROM 产品表 WHERE 产品名称 LIKE %旗舰% ESCAPE ! AND 产品名称 LIKE %2024% ESCAPE !;这个写法看着没什么特别但配合转义字符后用户输入再怎么刁钻都不会把查询搞崩。我接手过的系统里有好几处线上问题就是关键词里的%号直接拼进LIKE导致的——有人查“进度100%完成”这种含百分号的关键词因为没转义直接把全表都搜了出来返回几万条数据应用层直接超时。这些坑说大不大但每次都能让人加班到深夜。LIKE和查询函数这块儿其实就是熟练度的问题。先记住四种通配符的区别再搞明白%放哪里影响索引最后把字符串函数、聚合函数、日期函数串联起来用日常开发里的数据查询需求基本能全覆盖。如果你在实际操作中遇到什么灵异现象多半都逃不出NULL、排序规则、隐式转换这三大元凶照着上面的排查路径走一遍大概率能定位到问题。
返回列表