1. 全站关键字搜索到底难在哪
全站关键字搜索,说白了就是给一个词,让它在整个数据库里所有表、所有文本字段里翻一遍,把命中的记录捞出来。听起来简单,真做起来坑不少。最直接的问题是:表结构不统一。用户表有username、email,订单表有order_no、remark,商品表有title、description,字段名、类型、数量全不一样。你没法写一条固定的 SQL 把全站都覆盖了。
第二个问题是字段类型混杂。同样是文本,有的是varchar,有的是nvarchar,还有char、nchar,甚至text。如果拼接 SQL 时不加判断,遇到非字符型字段直接报类型转换错误。第三个问题是表数量多。一个中等规模的业务库,几十上百张表很正常,手工写 union 不现实,维护成本也高。
所以需要一个能自动遍历所有表、自动识别文本字段、动态拼 SQL 的机制。存储过程 + 游标 + 动态 SQL 就是干这个的。存储过程负责封装逻辑,游标负责逐表逐字段遍历,动态 SQL 负责在运行时拼出针对每张表每个字段的查询语句。三者配合,才能做到“给一个词,全库搜”。
这套方案适合谁?适合数据库侧做轻量级搜索、不想引入 Elasticsearch 这类外部组件的团队。数据量在百万级以内、对搜索实时性要求不极端的场景,用存储过程完全够用。下面我把可复制的骨架、游标遍历、动态 SQL 拼接、以及接入 TaoToken 统一 Key 通道的配置片段都写出来,你可以直接拿去改。
2. TaoToken 前置:统一 Key 与 API 通道准备
在讲存储过程之前,先把 TaoToken 的接入准备好。为什么要在数据库搜索方案里提 TaoToken?因为很多团队搜完数据后,需要把结果喂给模型做摘要、分类、或者生成自然语言描述。TaoToken 提供统一的 Key 和 API 通道,省得你在多个模型供应商之间来回切换配置。
TaoToken 官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。你需要先去控制台创建一个 API Key,然后就可以用同一个 Key 调用不同模型。
具体操作路径:进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 生成一个新的 Key。生成后复制保存,后面配置里要用。
如果你只是想在浏览器里先验证模型能不能通,可以直接用模型对话页面 https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ,输入一句话看返回。确认通道没问题后,再回到代码里配置。
对于长期做编码、Agent 类任务的场景,可以了解 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合持续性的开发调用。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各语言的调用示例。ClaudeCodeAnthropic 相关配置参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite 。
注意:TaoToken 是统一的 API 通道,不是数据库组件。它的作用是在你搜索出结果之后,把结果送去模型处理。数据库侧的搜索逻辑仍然在存储过程里完成。
3. 可复制配置:存储过程骨架与游标遍历
先建存储过程。核心思路是两层游标:外层游标遍历所有用户表,内层游标遍历当前表的所有字符型字段。每进入一个字段,就拼一条like查询,用sp_executesql执行,统计命中数。命中数大于 0 就输出该字段的匹配记录。
SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON GO ALTER PROC [dbo].[Full_Search] @string VARCHAR(50) AS BEGIN SET NOCOUNT ON; DECLARE @tbname VARCHAR(128); DECLARE @colname VARCHAR(128); DECLARE @sql NVARCHAR(2000); DECLARE @hitCount INT; DECLARE @resultTable TABLE ( TableName VARCHAR(128), ColumnName VARCHAR(128), MatchValue NVARCHAR(500) ); -- 外层游标:遍历所有用户表 DECLARE tbroy CURSOR FOR SELECT name FROM sysobjects WHERE xtype = 'U' ORDER BY name; OPEN tbroy; FETCH NEXT FROM tbroy INTO @tbname; WHILE @@FETCH_STATUS = 0 BEGIN -- 内层游标:遍历当前表的字符型字段 DECLARE colroy CURSOR FOR SELECT c.name FROM syscolumns c INNER JOIN systypes t ON c.xtype = t.xtype WHERE c.id = OBJECT_ID(@tbname) AND t.name IN ('varchar', 'nvarchar', 'char', 'nchar'); OPEN colroy; FETCH NEXT FROM colroy INTO @colname; WHILE @@FETCH_STATUS = 0 BEGIN SET @sql = N'SELECT @cnt = COUNT(1) FROM [' + @tbname + N'] WHERE [' + @colname + N'] LIKE @kw'; EXEC sp_executesql @sql, N'@kw NVARCHAR(100), @cnt INT OUTPUT', @kw = N'%' + @string + N'%', @cnt = @hitCount OUTPUT; IF @hitCount > 0 BEGIN INSERT INTO @resultTable (TableName, ColumnName, MatchValue) EXEC( N'SELECT ''' + @tbname + N''', ''' + @colname + N''', CAST([' + @colname + N'] AS NVARCHAR(500)) FROM [' + @tbname + N'] WHERE [' + @colname + N'] LIKE ''%' + @string + N'%''' ); END FETCH NEXT FROM colroy INTO @colname; END CLOSE colroy; DEALLOCATE colroy; FETCH NEXT FROM tbroy INTO @tbname; END CLOSE tbroy; DEALLOCATE tbroy; -- 输出汇总结果 SELECT TableName, ColumnName, MatchValue FROM @resultTable ORDER BY TableName, ColumnName; END GO这段代码有几个关键点。第一,用syscolumns和systypes联查,只取字符型字段,避免对int、datetime做like报错。第二,sp_executesql用参数化传@kw,比直接拼字符串安全,能防注入。第三,结果先插入表变量再统一输出,方便你后续加工。
如果你用的是 SQL Server 2005 及以上,sysobjects和syscolumns仍然可用,但更推荐用sys.tables和sys.columns。下面是兼容新版的字段遍历写法:
DECLARE colroy CURSOR FOR SELECT c.name FROM sys.columns c INNER JOIN sys.types t ON c.user_type_id = t.user_type_id WHERE c.object_id = OBJECT_ID(@tbname) AND t.name IN ('varchar', 'nvarchar', 'char', 'nchar');把这段替换掉原来的内层游标定义即可。实测下来,新版系统视图在字段类型判断上更准确,尤其是自定义类型的情况。
4. 验证请求与成功结果
存储过程建好后,直接调用测试。假设你要搜“订单”这个词:
EXEC dbo.Full_Search @string = '订单';执行后会返回一个结果集,三列:TableName、ColumnName、MatchValue。比如:
| TableName | ColumnName | MatchValue |
|---|---|---|
| Orders | Remark | 客户催订单发货 |
| Products | Title | 订单专用包装盒 |
| Logs | Content | 订单创建成功 |
这说明搜索通道跑通了。如果结果为空,先确认数据库里确实有包含该关键字的记录,再检查字段类型是否在varchar/nvarchar/char/nchar范围内。text和ntext类型不在当前游标范围内,需要单独处理。
接下来把搜索结果接入 TaoToken。假设你用 Python 做后端,搜完数据后调用模型做摘要:
import requests TAOTOKEN_API = "https://taotoken.net/api" API_KEY = "你的_TaoToken_Key" def summarize_search_results(keyword, results): prompt = f"以下是全站搜索关键字「{keyword}」的结果:\n" for r in results: prompt += f"- 表{r['TableName']} 字段{r['ColumnName']}:{r['MatchValue']}\n" prompt += "\n请用一段话总结这些结果的核心信息。" resp = requests.post( f"{TAOTOKEN_API}/v1/chat/completions", headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" }, json={ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": prompt}] } ) return resp.json()["choices"][0]["message"]["content"]调用后你会拿到一段自然语言摘要,比如“搜索结果显示订单相关记录集中在 Orders 表的 Remark 字段和 Products 表的 Title 字段,主要涉及发货催单和包装物料”。这样数据库搜索 + 模型摘要的链路就完整了。
提示:TaoToken 的 API 地址是 https://taotoken.net/api ,不要漏掉
/api路径。Key 放在Authorization头里,格式是Bearer 你的Key。
5. 本篇常见错排查
第一个常见错误:拒绝访问或对象名无效。这通常是存储过程创建时没有加dbo.前缀,或者当前登录用户没有执行权限。解决方法是创建时写CREATE PROC dbo.Full_Search,执行时写EXEC dbo.Full_Search。权限问题让 DBA 给EXECUTE权限即可。
第二个错误:将 varchar 值转换成 int 列时失败。这说明游标把非字符型字段也遍历进来了。检查systypes的过滤条件,确保只保留varchar、nvarchar、char、nchar。如果你用的是sys.types,注意用user_type_id关联,不要用xtype。
第三个错误:搜索结果重复。因为同一个值可能在多个字段命中,或者LIKE匹配到了多条记录。可以在最终输出时加DISTINCT,或者在插入表变量时去重。如果业务允许重复,保留原样也行。
第四个错误:执行超时。表多、数据量大时,逐字段COUNT会很慢。优化方向是限制遍历的表范围,比如只搜业务表,排除日志表、临时表。可以在外层游标加AND name NOT LIKE '%Log%'之类的过滤。另一个方向是给常用搜索字段建索引,但LIKE '%kw%'前置通配符用不上索引,这是LIKE的固有限制。
第五个错误:TaoToken 调用返回 401。检查 Key 是否复制完整,有没有多余空格。如果用的是环境变量,确认变量名和读取方式一致。401 一般是认证失败,403 可能是 Key 权限不足或额度用完。去控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 看一下 Key 状态和用量。
第六个错误:模型返回内容为空。检查请求体里model字段是否拼写正确,messages是否至少有一条user消息。如果用的是流式接口,解析方式不同,非流式接口直接取choices[0].message.content。
6. 接入文档与后续调用建议
存储过程跑通、TaoToken 调通之后,日常使用就是两步:先EXEC dbo.Full_Search @string = '关键词'拿结果,再把结果拼成 prompt 发给 TaoToken。如果你要做成 Web 服务,把这两步包在一个接口里,前端传关键词,后端返回摘要。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 Python、Node.js、Go 等语言的完整示例。API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,建议给不同环境建不同的 Key,方便排查和限额。
长期做编码类任务的话,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 比按次调用更划算。ClaudeCodeAnthropic 的配置参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite ,适合在 IDE 里直接调用。
最后说一个实用技巧:存储过程里的@string参数长度设成VARCHAR(50)可能不够,如果你的关键词可能更长,改成NVARCHAR(200)。另外,LIKE '%kw%'对大小写敏感取决于数据库排序规则,如果要不区分大小写,用COLLATE Chinese_PRC_CI_AS或者在拼接时统一转小写。这些细节调一次就记住了。