CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT,
author TEXT,
year INTEGER,
pages INTEGER
);
INSERT INTO books (title, author, year, pages) VALUES
('Dune', 'Herbert', 1965, 688),
('Neuromancer', 'Gibson', 1984, 271),
('Untitled Draft', 'Anonymous', NULL, 120);
-- Returns nothing, even though we have a NULL year:
SELECT title FROM books WHERE year = NULL;
-- This is the right way:
SELECT title FROM books WHERE year IS NULL;
SELECT title FROM books WHERE year IS NOT NULL;
IS NULL 和 IS NOT NULL 是仅有的能直接检测 NULL 的运算符。把它们刻进肌肉记忆:其他任何和 NULL 的比较都会返回 NULL,并悄悄丢掉这些行。
否定条件也适用同样的规则。WHERE author != 'Asimov' 不会返回 author IS NULL 的行,因为 NULL != 'Asimov' 的结果同样是 NULL。如果你想把 NULL 也包含进来,就要明确写出来:WHERE author != 'Asimov' OR author IS NULL。
IN 和 BETWEEN:天天都会用的简写
IN 检查某个值是否在列表中,比写一串 OR 更简洁:
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT,
author TEXT,
year INTEGER,
pages INTEGER
);
INSERT INTO books (title, author, year, pages) VALUES
('Dune', 'Herbert', 1965, 688),
('Neuromancer', 'Gibson', 1984, 271),
('Foundation', 'Asimov', 1951, 244),
('Hyperion', 'Simmons', 1989, 482),
('The Martian', 'Weir', 2011, 369);
SELECT title, author FROM books
WHERE author IN ('Herbert', 'Asimov', 'Weir');
SELECT title FROM books
WHERE year NOT IN (1965, 1984);
BETWEEN 检查一个范围,两端都包含在内:
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT,
author TEXT,
year INTEGER,
pages INTEGER
);
INSERT INTO books (title, author, year, pages) VALUES
('Dune', 'Herbert', 1965, 688),
('Neuromancer', 'Gibson', 1984, 271),
('Foundation', 'Asimov', 1951, 244),
('Hyperion', 'Simmons', 1989, 482),
('The Martian', 'Weir', 2011, 369);
SELECT title, year FROM books
WHERE year BETWEEN 1980 AND 2000;
SELECT title, pages FROM books
WHERE pages NOT BETWEEN 300 AND 500;
year BETWEEN 1980 AND 2000 与 year >= 1980 AND year <= 2000 完全等价,只是更短。要记住一点:上下界都包含在内。如果你想排除边界,就把比较条件分开写。
关于 IN 和 NULL 再补充一句:WHERE column NOT IN (1, 2, NULL) 永远不会返回任何行,因为任何值和 NULL 比较都得到 NULL。要么把 NULL 从列表里去掉,要么用 IS NULL 单独处理。
用 LIKE 做模式匹配
LIKE 用两种通配符做字符串模式匹配:
% 匹配任意长度的字符序列(包括空)。
_ 恰好匹配一个字符。
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT,
author TEXT,
year INTEGER,
pages INTEGER
);
INSERT INTO books (title, author, year, pages) VALUES
('Dune', 'Herbert', 1965, 688),
('Neuromancer', 'Gibson', 1984, 271),
('Foundation', 'Asimov', 1951, 244),
('Hyperion', 'Simmons', 1989, 482),
('The Martian', 'Weir', 2011, 369);
-- Titles starting with "The":
SELECT title FROM books WHERE title LIKE 'The %';
-- Titles containing "ion" anywhere:
SELECT title FROM books WHERE title LIKE '%ion%';
-- Four-letter author names:
SELECT author FROM books WHERE author LIKE '____';
默认情况下,SQLite 的 LIKE 对 ASCII 字母不区分大小写,'Dune' LIKE 'dune' 为真。如果你之前用的是 Postgres,这会让你意外:在 Postgres 中 LIKE 区分大小写,不区分大小写的版本是 ILIKE。(SQLite 没有 ILIKE。)
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT,
author TEXT,
year INTEGER,
pages INTEGER
);
INSERT INTO books (title, author, year, pages) VALUES
('Dune', 'Herbert', 1965, 688),
('Neuromancer', 'Gibson', 1984, 271),
('Foundation', 'Asimov', 1951, 244),
('Hyperion', 'Simmons', 1989, 482),
('The Martian', 'Weir', 2011, 369);
SELECT title FROM books WHERE title GLOB 'The *';
SELECT title FROM books WHERE title GLOB 'D*';
在这里 GLOB 'd*' 什么也匹配不到,因为大小写很重要。
筛选日期
SQLite 把日期存成文本(通常是 YYYY-MM-DD 或完整的 ISO 8601 格式),这意味着只要坚持使用 ISO 格式,字符串比较恰好也能当作日期比较:
CREATE TABLE events (
id INTEGER PRIMARY KEY,
name TEXT,
occurred_on TEXT
);
INSERT INTO events (name, occurred_on) VALUES
('Launch', '2024-01-15'),
('Patch', '2024-03-22'),
('Release', '2024-11-08'),
('Hotfix', '2025-02-01');
SELECT name FROM events
WHERE occurred_on >= '2024-06-01';
SELECT name FROM events
WHERE occurred_on BETWEEN '2024-01-01' AND '2024-12-31';
因为 '2024-06-01' < '2024-11-08' 无论按字符串还是按日期比较都为真,所以这些查询能得到预期的结果。如果你用其他格式存日期('15/01/2024'、'Jan 15 2024'),比较结果就会悄悄出错。始终使用 ISO 8601,将来的你会感谢现在的自己。
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT,
author TEXT,
year INTEGER,
pages INTEGER
);
INSERT INTO books (title, author, year, pages) VALUES
('Dune', 'Herbert', 1965, 688),
('Neuromancer', 'Gibson', 1984, 271),
('Foundation', 'Asimov', 1951, 244),
('Hyperion', 'Simmons', 1989, 482),
('The Martian', 'Weir', 2011, 369),
('Untitled Draft', 'Anonymous', NULL, 120);
SELECT title, author, year, pages
FROM books
WHERE year IS NOT NULL
AND year BETWEEN 1950 AND 2000
AND (author IN ('Herbert', 'Gibson') OR pages > 400)
AND title NOT LIKE '%Draft%';
逐行来读:保留年份已知、在范围内、出自两位作者之一或者篇幅够长、并且不是草稿的行。这正是 WHERE 子句最拿手的事:把一个个小而易读的条件组合成精确的筛选。