我们将从最基础的开始,逐步深入到更实用、更高效的实现方式,本教程假设您已经具备基础的 HTML、VBScript 和 ADO(ActiveX Data Objects)知识,并且已经搭建好了 ASP 的运行环境(如 IIS)。


教程大纲

  1. 准备工作
    • 创建一个示例数据库和数据表。
    • 建立数据库连接文件。
  2. 基础教程:简单的关键词搜索
    • 创建搜索页面 (search.asp)。
    • 创建结果显示页面 (results.asp)。
    • 代码解析。
  3. 进阶教程:更友好的搜索界面
    • 将搜索框和结果显示整合到同一页面。
    • 添加分页功能。
  4. 高级教程:模糊搜索与 LIKE 操作符
    • 使用 LIKE 实现模糊匹配。
    • 处理 SQL 注入风险。
  5. 终极优化:使用全文索引搜索
    • 介绍全文索引的优势。
    • 如何在 SQL Server 中创建和使用全文索引。
  6. 总结与最佳实践

准备工作

在开始之前,我们需要一个数据库和一些数据来搜索。

a. 创建示例数据库和数据表

我们以 SQL Server 为例,创建一个名为 mydb 的数据库,并在其中创建一个 Articles 表。

-- 创建数据库 (如果不存在)
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'mydb')
BEGIN
    CREATE DATABASE mydb;
END
GO
USE mydb;
GO
-- 创建文章表
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Articles' and xtype='U')
BEGIN
    CREATE TABLE Articles (
        id INT PRIMARY KEY IDENTITY(1,1),
        title NVARCHAR(255) NOT NULL,
        content NVARCHAR(MAX) NOT NULL,
        category NVARCHAR(100),
        create_date DATETIME DEFAULT GETDATE()
    );
END
GO
-- 插入一些示例数据
INSERT INTO Articles (title, content, category) VALUES
('ASP 基础教程', '这是一个关于如何开始学习 ASP 编程的入门教程,涵盖了变量、循环和条件语句等基本概念。', '编程'),
('SQL Server 查询优化', '本文详细讲解了如何优化 SQL 查询,包括索引使用、查询计划分析等高级技巧。', '数据库'),
('HTML5 新特性', 'HTML5 带来了许多新特性,如语义化标签、Canvas 绘图、本地存储等,极大地丰富了网页开发。', '前端'),
('VBScript 函数大全', '本手册收集了常用的 VBScript 内置函数,包括字符串处理、日期时间、数学计算等函数的用法和示例。', '编程');
GO

b. 建立数据库连接文件

为了方便管理和复用,我们将数据库连接信息写在一个单独的文件中,创建一个名为 conn.asp 的文件:

conn.asp

<%
' 定义数据库连接字符串
' 请根据您的实际情况修改数据库名称、用户名和密码
Dim connStr
connStr = "Provider=SQLOLEDB;Data Source=(local);Initial Catalog=mydb;User ID=sa;Password=your_password;"
' 创建数据库连接对象
Dim conn
Set conn = Server.CreateObject("ADODB.Connection")
' 打开数据库连接
On Error Resume Next ' 防止因连接失败而显示详细错误信息
conn.Open connStr
If Err.Number <> 0 Then
    Response.Write("数据库连接失败!请检查 conn.asp 文件中的连接字符串。")
    Response.End
End If
On Error GoTo 0
%>

基础教程:简单的关键词搜索

这是最简单的搜索实现,用户输入关键词,然后搜索标题或内容。

a. 创建搜索页面 (search_form.html)

这是一个普通的 HTML 表单,用于收集用户输入。

search_form.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">ASP 搜索示例</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; }
        input[type="text"] { width: 300px; padding: 8px; }
        input[type="submit"] { padding: 8px 15px; }
    </style>
</head>
<body>
    <h1>文章搜索</h1>
    <form action="results.asp" method="get">
        <input type="text" name="keyword" placeholder="请输入搜索关键词..." required>
        <input type="submit" value="搜索">
    </form>
</body>
</html>

b. 创建结果显示页面 (results.asp)

这个页面接收表单提交的关键词,并从数据库中查询结果。

results.asp

<%@ Language="VBScript" %>
<!-- #include file="conn.asp" -->
<%
' 1. 获取从 search_form.html 传递过来的关键词
Dim keyword
keyword = Request.QueryString("keyword")
' 2. 对关键词进行简单的过滤(防止SQL注入,后面会详细讲)
If keyword <> "" Then
    keyword = Replace(keyword, "'", "''") ' 将单引号替换为两个单引号
Else
    Response.Write("请输入搜索关键词!")
    Response.End
End If
' 3. 构建SQL查询语句
' 使用 LIKE 和 % 实现模糊匹配
Dim sql
sql = "SELECT id, title, category, create_date FROM Articles WHERE title LIKE '%" & keyword & "%' OR content LIKE '%" & keyword & "%' ORDER BY create_date DESC"
' 4. 执行查询并显示结果
Dim rs ' Recordset 对象
Set rs = Server.CreateObject("ADODB.Recordset")
Response.Write("<h1>搜索结果: """ & keyword & """</h1>")
Response.Write("<hr>")
rs.Open sql, conn
' 检查是否有记录
If rs.EOF Then
    Response.Write("没有找到相关文章。")
Else
    ' 循环输出每一条记录
    Do While Not rs.EOF
        Response.Write("<div style='border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;'>")
        Response.Write("<h3><a href='view_article.asp?id=" & rs("id") & "'>" & rs("title") & "</a></h3>")
        Response.Write("<p>分类: " & rs("category") & " | 发布日期: " & rs("create_date") & "</p>")
        Response.Write("</div>")
        rs.MoveNext ' 移动到下一条记录
    Loop
End If
' 5. 关闭记录集和连接
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
%>

代码解析:

  1. #include file="conn.asp": 引用我们之前创建的连接文件,这样就可以直接使用 conn 对象了。
  2. Request.QueryString("keyword"): 获取 URL 中 keyword 参数的值(因为 method="get")。
  3. Replace(keyword, "'", "''"): 这是一个简单的防 SQL 注入措施,它将用户输入的单引号()替换成两个单引号(),这在 SQL Server 中可以转义单引号,从而破坏恶意的 SQL 语句结构。
  4. LIKE '%' & keyword & '%': 这是 SQL 中的模糊匹配操作符。
    • LIKE '关键词': 精确匹配。
    • LIKE '%关键词%': 关键词出现在字符串的任意位置。
    • LIKE '关键词%': 关键词出现在字符串的开头。
    • LIKE '%关键词': 关键词出现在字符串的结尾。
  5. rs.EOF: EOF (End of File) 属性用于判断记录集是否已经到达末尾,如果到达末尾,说明没有查询到结果。
  6. Do While Not rs.EOF ... Loop: 一个标准的循环,用于遍历记录集中的所有记录。
  7. rs.MoveNext: 将记录集指针移动到下一条记录。

进阶教程:更友好的搜索界面

将搜索和结果显示放在同一个页面,并添加分页功能,这是实际应用中最常见的需求。

a. 整合搜索与结果页面 (search_advanced.asp)

search_advanced.asp

<%@ Language="VBScript" %>
<!-- #include file="conn.asp" -->
<%
' 获取搜索关键词和当前页码
Dim keyword, page, pageSize, rs, sql, totalRecords
keyword = Request.Form("keyword") ' 使用 POST 方法
page = CInt(Request.QueryString("page")) ' 当前页码
If page <= 0 Then page = 1
' 每页显示的记录数
pageSize = 5
If keyword <> "" Then
    keyword = Replace(keyword, "'", "''")
    ' 1. 获取总记录数
    sql = "SELECT COUNT(*) FROM Articles WHERE title LIKE '%" & keyword & "%' OR content LIKE '%" & keyword & "%'"
    Set rs = conn.Execute(sql)
    totalRecords = rs(0)
    rs.Close
    ' 2. 获取当前页的记录
    sql = "SELECT id, title, category, create_date FROM Articles WHERE title LIKE '%" & keyword & "%' OR content LIKE '%" & keyword & "%' ORDER BY create_date DESC"
    Set rs = Server.CreateObject("ADODB.Recordset")
    rs.PageSize = pageSize
    rs.CacheSize = pageSize
    rs.Open sql, conn, 1, 1 ' 1,1 表示只读、使用游标
    ' 检查页码是否超出范围
    If page > rs.PageCount Then page = rs.PageCount
    ' 设置当前页
    rs.AbsolutePage = page
    ' 3. 显示结果
    Response.Write("<h1>搜索结果: """ & keyword & """</h1>")
    Response.Write("<p>找到 " & totalRecords & " 条相关记录。</p>")
    Response.Write("<hr>")
    If rs.EOF Then
        Response.Write("没有找到相关文章。")
    Else
        Do While Not rs.EOF And rs.PageSize > 0
            Response.Write("<div style='border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;'>")
            Response.Write("<h3><a href='view_article.asp?id=" & rs("id") & "'>" & rs("title") & "</a></h3>")
            Response.Write("<p>分类: " & rs("category") & " | 发布日期: " & rs("create_date") & "</p>")
            Response.Write("</div>")
            rs.MoveNext
            rs.PageSize = rs.PageSize - 1 ' 控制循环次数
        Loop
    End If
    ' 4. 显示分页导航
    If rs.PageCount > 1 Then
        Response.Write("<div style='margin-top: 20px;'>")
        Response.Write("第 " & page & " 页 / 共 " & rs.PageCount & " 页 | ")
        ' 上一页
        If page > 1 Then
            Response.Write("<a href='search_advanced.asp?keyword=" & Server.URLEncode(keyword) & "&page=" & (page-1) & "'>上一页</a> | ")
        End If
        ' 下一页
        If page < rs.PageCount Then
            Response.Write("<a href='search_advanced.asp?keyword=" & Server.URLEncode(keyword) & "&page=" & (page+1) & "'>下一页</a>")
        End If
        Response.Write("</div>")
    End If
    rs.Close
    Set rs = Nothing
Else
    ' 如果没有关键词,显示搜索表单
%>
    <h1>文章搜索</h1>
    <form action="search_advanced.asp" method="post">
        <input type="text" name="keyword" placeholder="请输入搜索关键词..." value="<%=keyword%>">
        <input type="submit" value="搜索">
    </form>
<%
End If
conn.Close
Set conn = Nothing
%>

关键改进:

  1. 整合界面: 使用 Request.Form("keyword") 获取表单数据,并根据 keyword 是否为空来决定是显示搜索框还是显示结果。
  2. 分页功能:
    • rs.PageSize = pageSize: 设置每页显示的记录数。
    • rs.PageCount: 获取总页数。
    • rs.AbsolutePage = page: 将指针定位到指定页。
    • Do While ... rs.PageSize > 0: 通过一个计数器来精确控制每页只显示 pageSize 条记录。
  3. 分页导航: 生成了“上一页”和“下一页”的链接。
  4. Server.URLEncode(keyword): 在生成链接时对中文关键词进行 URL 编码,防止特殊字符导致链接错误。

高级教程:模糊搜索与 LIKE 操作符

基础教程中我们已经使用了 LIKE,这里我们再深入探讨一下它的用法和安全性。

a. LIKE 的模式

LIKE '%asp%' 搜索标题中包含 "asp" 的所有文章,LIKE 'ASP%': 搜索标题以 "ASP" 开头的文章,LIKE '%教程'`: 搜索标题以 "教程" 结尾的文章。

b. 处理 SQL 注入 (非常重要!)

我们之前用的 Replace(keyword, "'", "''") 是一种防御方式,但更安全、更推荐的方式是 使用参数化查询 (Parameterized Queries)

参数化查询可以彻底杜绝 SQL 注入,因为它将 SQL 语句和数据分开处理。

使用参数化查询重写 results.asp 的核心部分:

' ... (获取 keyword 的代码不变) ...
If keyword <> "" Then
    ' 1. 构建SQL查询语句 (不直接拼接字符串)
    Dim sql, cmd
    sql = "SELECT id, title, category, create_date FROM Articles WHERE title LIKE @keyword OR content LIKE @keyword ORDER BY create_date DESC"
    ' 2. 创建 Command 对象
    Set cmd = Server.CreateObject("ADODB.Command")
    cmd.ActiveConnection = conn
    cmd.CommandText = sql
    cmd.CommandType = 1 ' 1 表示 adCmdText
    ' 3. 添加参数
    ' 注意:LIKE 查询的参数值需要我们自己加上 % 符号
    cmd.Parameters.Append cmd.CreateParameter("@keyword", 200, 1, 255, "%" & keyword & "%") ' 200=adVarWChar, 1=adParamInput
    ' 4. 执行查询
    Set rs = cmd.Execute()
    ' ... (后面的显示结果代码不变) ...
    ' 5. 关闭对象
    rs.Close
    Set rs = Nothing
    Set cmd = Nothing
End If

参数化查询的优势:

  • 安全性: 数据库驱动会自动处理参数值的转义,黑客无法通过输入特殊字符来篡改 SQL 语句结构。
  • 性能: 对于重复执行的 SQL 语句(只是参数不同),数据库可以缓存查询计划,提高执行效率。
  • 可读性: SQL 语句和数据分离,代码更清晰。

终极优化:使用全文索引搜索

当数据量非常大时(文章表有几十万甚至上百万条记录),使用 LIKE '%...%' 会导致全表扫描,性能极差,这时,全文索引 (Full-Text Indexing) 是最佳解决方案。

全文索引会为文本列建立一个特殊的索引,使得搜索速度非常快,并且支持更复杂的搜索(如单词搜索、词组搜索、加权搜索等)。

a. 在 SQL Server 中创建全文索引

  1. 启用数据库的全文索引功能:

    USE mydb;
    GO
    -- 如果尚未启用,执行此语句
    -- EXEC sp_fulltext_database 'enable';
    GO
  2. 创建全文目录 (用于存储全文索引文件):

    -- 如果不存在名为 my_ft_catalog 的全文目录,则创建
    IF NOT EXISTS (SELECT * FROM sys.fulltext_catalogs WHERE name = 'my_ft_catalog')
    BEGIN
        CREATE FULLTEXT CATALOG my_ft_catalog AS DEFAULT;
    END
    GO
  3. Articles 表上创建全文索引:

    -- 首先确保表有一个唯一、非空的列作为全文索引的键 (主键 id 已经满足)
    -- 然后为 title 和 content 列创建全文索引
    CREATE FULLTEXT INDEX ON Articles (title, content LANGUAGE 2052) -- 2052 是简体中文的语言ID
        KEY INDEX PK__Articles__3214EC27; -- PK__Articles__3214EC27 是 Articles 表主键约束的名称
    GO
    • LANGUAGE 2052: 指定语言为简体中文,这对于分词(将句子切分成单词)很重要。
    • KEY INDEX: 指定全文索引所基于的唯一键。

b. 在 ASP 中使用 CONTAINS 进行搜索

创建了全文索引后,我们就可以使用 CONTAINSFREETEXT 谓词来代替 LIKE

  • CONTAINS: 用于精确的单词或短语匹配。CONTAINS(title, '"ASP教程"') 会搜索标题中包含 "ASP教程" 这个完整词组的内容。
  • FREETEXT: 更像自然语言的搜索,会进行词干提取和同义词扩展。FREETEXT(content, '学习编程') 会搜索内容中与 "学习"、"编程" 相关的词。

使用 FREETEXT 重写搜索逻辑 (search_fulltext.asp):

<%@ Language="VBScript" %>
<!-- #include file="conn.asp" -->
<%
Dim keyword
keyword = Request.Form("keyword")
If keyword <> "" Then
    keyword = Replace(keyword, "'", "''")
    ' 使用 FREETEXT 进行全文搜索
    ' 它会同时在 title 和 content 列中搜索
    Dim sql, rs
    sql = "SELECT id, title, category, create_date FROM Articles WHERE FREETEXT(*, '" & keyword & "') ORDER BY create_date DESC"
    ' 强烈建议这里也使用参数化查询!
    ' sql = "SELECT ... FROM Articles WHERE FREETEXT(*, @search_term)"
    ' Set cmd = ...
    ' cmd.Parameters.Append cmd.CreateParameter("@search_term", 200, 1, 255, keyword)
    ' Set rs = cmd.Execute()
    Set rs = conn.Execute(sql)
    ' ... (显示结果的代码与之前相同) ...
    rs.Close
    Set rs = Nothing
Else
    ' ... (显示搜索表单的代码与之前相同) ...
End If
conn.Close
Set conn = Nothing
%>

全文索引的优势:

  • 速度极快: 对于海量数据,搜索速度比 LIKE 快几个数量级。
  • 功能强大: 支持布尔运算符(AND, OR, NOT)、邻近搜索(NEAR)、加权搜索等。
  • 语言智能: 支持多种语言的词干提取和同义词处理。

总结与最佳实践

  1. 对于小型网站或少量数据: 使用 LIKE 模糊搜索 配合 参数化查询 是一个简单、快速且安全的方案。
  2. 对于中大型网站或海量数据: 必须使用全文索引,这是保证搜索性能和用户体验的唯一途径,请务必学会 CONTAINSFREETEXT 的用法。
  3. 安全性永远是第一位: 始终坚持使用 参数化查询,不要相信任何用户输入,不要直接将其拼接到 SQL 语句中。
  4. 用户体验: 将搜索和结果显示整合到同一页面,并添加 分页功能,这是现代网站的基本要求。
  5. 扩展性: 如果需求更复杂(如搜索建议、高亮显示、按相关性排序),可以考虑集成专业的搜索引擎,如 ElasticsearchSolr,但这已经超出了传统 ASP 的范畴。

希望这个详细的教程能帮助您在 ASP 项目中成功实现搜索功能!