🇯🇵 日本語 | 🇺🇸 English | 🇪🇸 Español | 🇵🇹 Português | 🇹🇭 ไทย | 🇨🇳 中文

【SQL LIKE子句】掌握模糊匹配!`%`与`_`通配符的用法

网站的搜索功能是必不可少的,对吧?我们经常会遇到想要查找“不完全匹配,但部分内容相符”的数据的场景,比如“我想找标题包含‘SQL’这个词的书”、“我想列出所有姓‘铃木’的用户”。在WHERE子句中使用=(等于)可以找到完全匹配的数据,但像这样的“模糊搜索”或“部分匹配搜索”该如何实现呢?

答案就是SQL的LIKE子句。LIKE子句与WHERE子句结合使用,能够以字符串的部分匹配作为条件来提取数据。而能极大地提升其搜索能力的,就是被称为通配符的特殊符号。就像扑克牌里的王牌一样,只要你能熟练运用这些可以代表任何字符的符号,你的数据搜索将变得更加自由和强大。

本文将通过大量可即拷即用的代码,从LIKE子句的基本用法,到两种主要通配符(%_)的区分使用,再到实际工作中有用的注意事项,进行全面而详尽的解说。来吧,以成为搜索达人为目标,一同探索LIKE子句的世界吧!


准备工作:创建用于搜索的书籍数据

为了尝试部分匹配搜索,我们首先需要准备一些用于搜索的数据。这次,我们将创建一个简单的books表来管理书籍列表。为了能够尝试各种搜索模式,我们会特意注册一些标题相似或包含特殊符号的书籍。

-- 如果books表已存在,则删除(以便重复测试)
DROP TABLE IF EXISTS books;

-- 创建一个新的books表
CREATE TABLE books (
  id INTEGER PRIMARY KEY,
  title TEXT NOT NULL,
  author TEXT NOT NULL,
  price INTEGER
);

-- 插入初始数据
INSERT INTO books (id, title, author, price) VALUES
(1, 'SQL入门', '山田 太郎', 2500),
(2, '实践!SQL数据分析', '鈴木 一郎', 3200),
(3, 'Web设计教科书', '佐藤 花子', 2800),
(4, '快乐SQL入门', '山田 太郎', 2200),
(5, '网站制作指南', '佐藤 次郎', 3000),
(6, '增长率120%的营销术', '田中 実', 1800),
(7, '续·SQL入门', '山田 太郎', 2600);

现在,我们已经准备好用各种关键词来尝试搜索了。


通配符①:`%` (百分号) - 任意数量的任意字符

%LIKE子句中最常用的通配符。它代表“任意长度(包括零长度)、任意类型的字符串”。也就是说,你可以把它想象成一张万能的王牌。

前缀匹配:查找以“...”开头的数据

使用'搜索字符串%'的格式,可以查找以指定字符串开头的数据。

场景:“我想找所有标题以‘SQL’开头的书。”

SELECT * FROM books WHERE title LIKE 'SQL%';

执行结果:

id | title    | author    | price
---|----------|-----------|-------
1  | SQL入门  | 山田 太郎 | 2500

ID为1的《SQL入门》被找到了。《快乐SQL入门》和《实践!SQL数据分析》因为不以"SQL"开头,所以没有包含在搜索结果中。


后缀匹配:查找以“...”结尾的数据

使用'%搜索字符串'的格式,可以查找以指定字符串结尾的数据。

场景:“我想找所有作者名以‘太郎’结尾的书。”

SELECT * FROM books WHERE author LIKE '%太郎';

执行结果:

id | title      | author    | price
---|------------|-----------|-------
1  | SQL入门    | 山田 太郎 | 2500
4  | 快乐SQL入门| 山田 太郎 | 2200
7  | 续·SQL入门 | 山田 太郎 | 2600

“山田 太郎”的所有书籍都被列出来了。


包含匹配:查找“包含...”的数据

这是最通用的用法。通过将搜索关键词用%包裹起来,如'%搜索字符串%',可以找出所有在任何位置包含指定字符串的数据。

场景:“我想找所有标题中包含‘SQL’这个词的书。”

SELECT * FROM books WHERE title LIKE '%SQL%';

执行结果:

id | title          | author    | price
---|----------------|-----------|-------
1  | SQL入门        | 山田 太郎 | 2500
2  | 实践!SQL数据分析| 鈴木 一郎 | 3200
4  | 快乐SQL入门    | 山田 太郎 | 2200
7  | 续·SQL入门     | 山田 太郎 | 2600

这次,无论"SQL"出现在标题的哪个位置,都会被包含在搜索结果中。网站搜索功能的背后,就经常使用这样的查询。


通配符②:`_` (下划线) - 任意单个字符

另一个重要的通配符是_(下划线)。与%代表零个或多个“任意”字符不同,_代表“恰好一个任意字符”。字符数固定为一个,是它们之间最大的区别。

这在需要进行精细搜索时很有用,比如区分发音相似但字数不同的名字。

场景:“我想找姓‘佐藤’且名字为两个汉字的作者(例如:佐藤 花子, 佐藤 次郎)。”

-- '佐藤 '后面跟着两个任意字符的模式
SELECT * FROM books WHERE author LIKE '佐藤 __';

执行结果:

id | title          | author    | price
---|----------------|-----------|-------
3  | Web设计教科书  | 佐藤 花子 | 2800
5  | 网站制作指南   | 佐藤 次郎 | 3000

通过连续使用两个_,我们指定了“任意两个字符”,从而找到了“佐藤 花子”和“佐藤 次郎”。如果我们使用LIKE '佐藤 _',那么只会匹配到名字为一个字的人(如果存在的话)。


应用:当需要搜索'%'或'_'本身时 (`ESCAPE`子句)

现在,我们来解决一个稍微棘手的问题。如果你想找“标题中包含‘120%’这个字符串的书”,该怎么办呢?

如果不加思考地写成WHERE title LIKE '%120%%',那么中间的%也会被解释为通配符,导致无法得到预期的搜索结果。在这种需要将通配符本身作为“普通字符”来搜索的情况下,ESCAPE子句就登场了。

ESCAPE子句允许你自定义一个特殊的“转义字符”,它告诉数据库:“紧跟在这个字符后面的通配符将失效(被转义),只作为普通字符处理”。

-- 定义'!'为转义字符
SELECT * FROM books WHERE title LIKE '%120!%%' ESCAPE '!';

在这个查询中,ESCAPE '!'!指定为转义字符。因此,!%这部分的意思就变成了“这不是一个通配符,只是一个普通的'%'字符”。结果,只有ID为6的《增长率120%的营销术》被正确地匹配到了。

常用的转义字符有!#\(反斜杠)等。


【体验环节】在浏览器中运行SQL,尝试部分匹配搜索!

是时候把知识变成技能了!将下面的HTML代码完整复制,保存为sql_like_test.html之类的文件名,然后在浏览器中打开它。你专属的SQL执行环境将会启动,并且本文中使用的books表已经为你准备好了。

试着改变%_的位置,或者更换关键词,看看搜索结果会如何变化。成为搜索达人的道路,就在于不断的尝试!

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SQL LIKE子句 在线执行环境</title>
  <style>
    body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; line-height: 1.7; color: #333; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
    h1 { color: #3498db; }
    textarea { width: 100%; height: 180px; font-family: "SF Mono", "Consolas", monospace; font-size: 16px; padding: 12px; border: 1px solid #ccc; border-radius: 6px; box-sizing: border-box; margin-bottom: 1rem; }
    button { background-color: #3498db; color: white; border: none; padding: 12px 22px; font-size: 16px; border-radius: 6px; cursor: pointer; transition: background-color 0.2s; }
    button:hover { background-color: #2980b9; }
    button:disabled { background-color: #bdc3c7; cursor: not-allowed; }
    #result-container { margin-top: 2rem; border: 1px solid #ddd; padding: 1rem; border-radius: 6px; background: #fdfdfd; min-height: 50px; }
    #error-message { color: #e74c3c; font-weight: bold; }
    table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
    th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
    th { background-color: #f2f2f2; }
    tr:nth-child(even) { background-color: #f9f9f9; }
  </style>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/sql-wasm.js"></script>
</head>
<body>

  <h1>来试试SQL吧!</h1>
  <p>在下方的文本框中输入SQL语句,然后点击“执行”按钮。试试用不同的关键词搜索吧!</p>

  <textarea id="sql-input">-- 找找看标题里包含“Web”的书
SELECT * FROM books WHERE title LIKE '%Web%';
  </textarea>
  
  <button id="execute-btn">执行</button>
  
  <div id="result-container">
    <p id="error-message"></p>
    <div id="result-output"></div>
  </div>

  <script>
    const sqlInput = document.getElementById('sql-input');
    const executeBtn = document.getElementById('execute-btn');
    const errorMsg = document.getElementById('error-message');
    const resultOutput = document.getElementById('result-output');

    let db;

    async function initDb() {
      executeBtn.disabled = true;
      executeBtn.textContent = '数据库准备中...';
      try {
        const SQL = await initSqlJs({
          locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/${file}`
        });
        db = new SQL.Database();
        
        const setupSql = `
          DROP TABLE IF EXISTS books;
          CREATE TABLE books (
            id INTEGER PRIMARY KEY,
            title TEXT NOT NULL,
            author TEXT NOT NULL,
            price INTEGER
          );
          INSERT INTO books (id, title, author, price) VALUES
          (1, 'SQL入门', '山田 太郎', 2500),
          (2, '实践!SQL数据分析', '鈴木 一郎', 3200),
          (3, 'Web设计教科书', '佐藤 花子', 2800),
          (4, '快乐SQL入门', '山田 太郎', 2200),
          (5, '网站制作指南', '佐藤 次郎', 3000),
          (6, '增长率120%的营销术', '田中 実', 1800),
          (7, '续·SQL入门', '山田 太郎', 2600);
        `;
        db.run(setupSql);
        
        executeBtn.disabled = false;
        executeBtn.textContent = '执行';
        resultOutput.innerHTML = '<p>准备就绪!请随意尝试你自己的SQL查询。</p>';

      } catch (err) {
        errorMsg.textContent = '数据库初始化失败:' + err.message;
        console.error(err);
      }
    }

    function executeSql() {
      if (!db) return;
      
      const sql = sqlInput.value;
      errorMsg.textContent = '';
      resultOutput.innerHTML = '';

      try {
        const results = db.exec(sql);
        if (results.length === 0) {
          resultOutput.innerHTML = '<p>查询成功执行,但没有返回结果集。</p>';
          return;
        }
        
        results.forEach(result => {
          const table = document.createElement('table');
          const thead = document.createElement('thead');
          const tbody = document.createElement('tbody');
          
          const headerRow = document.createElement('tr');
          result.columns.forEach(colName => {
            const th = document.createElement('th');
            th.textContent = colName;
            headerRow.appendChild(th);
          });
          thead.appendChild(headerRow);
          
          result.values.forEach(row => {
            const bodyRow = document.createElement('tr');
            row.forEach(cellValue => {
              const td = document.createElement('td');
              td.textContent = cellValue === null ? 'NULL' : cellValue;
              bodyRow.appendChild(td);
            });
            tbody.appendChild(bodyRow);
          });
          
          table.appendChild(thead);
          table.appendChild(tbody);
          resultOutput.appendChild(table);
        });

      } catch (err) {
        errorMsg.textContent = 'SQL错误:' + err.message;
        console.error(err);
      }
    }

    executeBtn.addEventListener('click', executeSql);
    
    initDb();
  </script>
</body>
</html>

注意事项:性能与大小写区分

LIKE子句非常方便,但在实际工作中使用时有几点需要注意。

如果想从大量文本数据中进行高速搜索,通常会使用比LIKE子句更高级的“全文搜索(Full-Text Search)”功能。首先掌握LIKE子句,然后将学习全文搜索作为下一个目标,会是个不错的选择。


总结

这次,我们围绕着LIKE子句的强大助手——通配符,学习了如何在SQL中进行部分匹配搜索。

熟练运用LIKE子句,是提供用户友好搜索功能的必备技能。请务必在本文的体验环节中尝试各种模式,亲身感受部分匹配搜索的感觉。你的SQL技能一定又上了一个新台阶!