目录

  1. 引言:精准筛选数据的利器——WHERE 子句
  2. WHERE 子句基础
    • 2.1 语法结构
    • 2.2 与 SELECT 语句的结合
  3. WHERE 子句中的运算符
    • 3.1 比较运算符 (=, !=, <, >, <=, >=)
    • 3.2 逻辑运算符 (AND, OR, NOT)
    • 3.3 范围运算符 (BETWEEN)
    • 3.4 集合运算符 (IN, NOT IN)
    • 3.5 模式匹配运算符 (LIKE, GLOB)
      • 3.5.1 LIKE 通配符 (%, _)
      • 3.5.2 GLOB 通配符 (*, ?, [])
      • 3.5.3 LIKEGLOB 的区别
    • 3.6 空值判断 (IS NULL, IS NOT NULL)
    • 3.7 存在性判断 (EXISTS, NOT EXISTS) (与子查询结合)
    • 3.8 大小写敏感性 (COLLATE NOCASE)
  4. WHERE 子句与其他 SQL 子句的结合
    • 4.1 WHEREORDER BY
    • 4.2 WHERELIMIT/OFFSET
    • 4.3 WHEREJOIN
  5. 在 Python sqlite3 中使用 WHERE 子句
    • 5.1 参数绑定:防止 SQL 注入的核心
      • 5.1.1 位置参数 (?)
      • 5.1.2 命名参数 (:name)
    • 5.2 执行查询与获取结果
    • 5.3 处理 Python 数据类型
  6. 性能优化:索引与 WHERE
    • 6.1 索引的重要性
    • 6.2 何时创建索引
  7. 错误处理
    • 7.1 sqlite3.OperationalError
  8. 最佳实践与注意事项
    • 8.1 再次强调:永远使用参数绑定!
    • 8.2 使用 with 语句管理资源
    • 理解 NULL 值的特殊性
    • 小心使用 LIKEGLOB (性能影响)
    • 优化查询性能:索引、特定列选择
    • 外键约束的启用
  9. 综合代码示例
    • 9.1 数据库结构和数据准备
    • 9.2 各种 WHERE 子句查询示例
  10. 总结

1. 引言:精准筛选数据的利器——WHERE 子句

在上一篇深度解析中,我们学习了如何使用 SELECT 语句从数据库中检索数据。然而,SELECT 语句常常会返回表中所有的行,这在大多数实际应用中并非所需。为了从海量数据中精确地找出我们关注的特定记录,SQL 提供了强大的 WHERE 子句

WHERE 子句允许您根据一个或多个条件过滤查询结果,确保只有满足这些条件的行才会被返回。它是 SQL 查询的核心组成部分之一,对于数据检索的效率和准确性至关重要。

本指南将详细讲解 WHERE 子句的各种用法,包括其支持的运算符、与其他 SQL 子句的结合,以及如何在 Python 的 sqlite3 模块中安全高效地应用它。

2. WHERE 子句基础

2.1 语法结构

WHERE 子句紧跟在 FROM 子句之后(或 JOIN 子句之后),它包含一个或多个布尔表达式,这些表达式评估为 TRUEFALSENULL。只有评估为 TRUE 的行才会被包含在结果集中。

SELECT column1, column2, ...
FROM table_name
WHERE condition;
  • condition: 一个或多个布尔表达式,可以包含列名、字面值、函数、运算符等。

2.2 与 SELECT 语句的结合

WHERE 子句可以与 SELECT 语句的任何形式结合使用,无论是选择所有列 (*) 还是特定列。

示例:

-- 选择所有客户,但只筛选出名为 'Alice' 的客户
SELECT *
FROM customers
WHERE first_name = 'Alice';

3. WHERE 子句中的运算符

WHERE 子句支持多种运算符来构建复杂的筛选条件。

3.1 比较运算符 (=, !=, <, >, <=, >=)

用于比较两个值。

  • =: 等于
  • != (或 <>): 不等于
  • <: 小于
  • >: 大于
  • <=: 小于等于
  • >=: 大于等于

示例:

SELECT name, price FROM products WHERE price >= 100;
SELECT first_name, last_name FROM customers WHERE registration_date < '2023-03-01';
SELECT order_id, total_amount FROM orders WHERE is_completed != 1; -- is_completed 是 BOOLEAN,存储为 0 或 1

3.2 逻辑运算符 (AND, OR, NOT)

用于组合多个条件。

  • AND: 逻辑与,只有当所有条件都为 TRUE 时,整个条件才为 TRUE
  • OR: 逻辑或,只要有一个条件为 TRUE,整个条件就为 TRUE
  • NOT: 逻辑非,反转条件的布尔值。

示例:

SELECT * FROM products WHERE stock > 50 AND price < 500;
SELECT * FROM customers WHERE first_name = 'Alice' OR last_name = 'Johnson';
SELECT * FROM orders WHERE NOT is_completed AND total_amount > 100; -- 未完成且金额大于 100 的订单

优先级: NOT > AND > OR。可以使用括号 () 来明确指定运算顺序。

3.3 范围运算符 (BETWEEN)

用于检查一个值是否在两个值之间(包含边界值)。

SELECT name, price FROM products WHERE price BETWEEN 50.00 AND 300.00; -- 价格在 50.00 到 300.00 之间

等价于:price >= 50.00 AND price <= 300.00

3.4 集合运算符 (IN, NOT IN)

用于检查一个值是否在一组给定值中。

SELECT first_name, last_name FROM customers WHERE first_name IN ('Alice', 'David', 'Eve');
SELECT name FROM products WHERE name NOT IN ('Laptop', 'Monitor');

3.5 模式匹配运算符 (LIKE, GLOB)

用于字符串的模糊匹配。

3.5.1 LIKE 通配符 (%, _)
  • %: 匹配零个、一个或多个任意字符。
  • _: 匹配任何单个字符。
SELECT email FROM customers WHERE email LIKE '%@example.com'; -- 查找所有以 @example.com 结尾的邮箱
SELECT name FROM products WHERE name LIKE 'M_u%';            -- 匹配 'Mouse', 'Mount' 等
3.5.2 GLOB 通配符 (*, ?, [])

GLOB 提供 Unix 文件名匹配语法。它通常区分大小写(除非数据库默认设置或使用 COLLATE NOCASE),而 LIKE 默认不区分大小写(取决于数据库配置,SQLite 默认不区分)。

  • *: 匹配零个或多个任意字符 (与 LIKE% 相似)。
  • ?: 匹配任何单个字符 (与 LIKE_ 相似)。
  • []: 匹配方括号内的任何单个字符。
  • [^]: 匹配不在方括号内的任何单个字符。
SELECT first_name FROM customers WHERE first_name GLOB 'A*'; -- 查找以 'A' 开头的所有名字
SELECT name FROM products WHERE name GLOB 'M?nitor';        -- 匹配 'Monitor', 'Manitor' 等
SELECT name FROM products WHERE name GLOB '[KL]*';           -- 匹配以 'K' 或 'L' 开头的产品
3.5.3 LIKEGLOB 的区别
  • 通配符: LIKE 使用 %_GLOB 使用 *?
  • 大小写敏感性: LIKE 默认不区分大小写(但在某些配置下可能区分);GLOB 默认区分大小写。如果需要 GLOB 不区分大小写,可以使用 COLLATE NOCASE

3.6 空值判断 (IS NULL, IS NOT NULL)

这是检查列是否为 NULL唯一正确方式。您不能使用 = NULL!= NULL,因为 NULL 表示“未知”,任何与 NULL 的比较都会得到 NULL (在布尔上下文中被视为 FALSE)。

SELECT customer_id, email FROM customers WHERE email IS NULL;      -- 查找邮箱为空的客户
SELECT customer_id, email FROM customers WHERE email IS NOT NULL;  -- 查找邮箱不为空的客户

3.7 存在性判断 (EXISTS, NOT EXISTS) (与子查询结合)

EXISTS 运算符与子查询一起使用,用于测试子查询是否返回任何行。如果子查询返回至少一行,EXISTS 返回 TRUE;否则返回 FALSE

-- 查找下过订单的客户
SELECT first_name, last_name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

-- 查找从未下过订单的客户
SELECT first_name, last_name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

3.8 大小写敏感性 (COLLATE NOCASE)

SQLite 默认的字符串比较规则是大小写不敏感的,但可以通过 COLLATE 关键字来指定排序规则。NOCASE 表示不区分大小写,BINARY 表示区分大小写。

SELECT first_name FROM customers WHERE first_name = 'alice' COLLATE NOCASE; -- 强制不区分大小写比较
SELECT first_name FROM customers WHERE first_name GLOB 'alice' COLLATE NOCASE; -- 强制 GLOB 不区分大小写

4. WHERE 子句与其他 SQL 子句的结合

WHERE 子句通常与其他 SQL 子句一起使用,以构建更复杂和精确的查询。

4.1 WHEREORDER BY

WHERE 子句过滤数据,然后 ORDER BY 子句对过滤后的结果进行排序。

SELECT name, price FROM products
WHERE stock > 100
ORDER BY price DESC;

4.2 WHERELIMIT/OFFSET

WHERE 子句过滤数据,然后 LIMITOFFSET 子句限制返回的行数和起始位置。

SELECT first_name, last_name FROM customers
WHERE registration_date > '2023-02-01'
ORDER BY registration_date ASC
LIMIT 3 OFFSET 0; -- 查找 2023 年 2 月 1 日后注册的前 3 位客户

4.3 WHEREJOIN

当使用 JOIN 连接多个表时,WHERE 子句可以对连接后的结果集进行过滤。

SELECT c.first_name, o.order_date, o.total_amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE o.total_amount > 500 AND o.is_completed = 0; -- 查找金额大于 500 且未完成的订单及其客户信息

5. 在 Python sqlite3 中使用 WHERE 子句

在 Python 中执行带 WHERE 子句的查询,关键在于使用参数绑定

5.1 参数绑定:防止 SQL 注入的核心

重要提示: 绝不将用户输入或其他动态值直接拼接到 SQL 查询字符串中。这会使您的应用程序容易受到 SQL 注入攻击。相反,请使用参数绑定。sqlite3 模块会自动处理值的转义和类型转换。

5.1.1 位置参数 (?)

使用问号 ? 作为占位符,将要替换的值作为元组传递给 cursor.execute() 方法的第二个参数。

customer_email = "alice@example.com"
cursor.execute("SELECT * FROM customers WHERE email = ?", (customer_email,)) # 注意:即使只有一个参数,也必须是元组
alice_data = cursor.fetchone()
5.1.2 命名参数 (:name)

使用 :param_name 作为占位符,将值作为字典传递。这种方式在参数多时可读性更高。

min_stock = 50
max_price = 100.0
cursor.execute("SELECT name, stock, price FROM products WHERE stock > :min_s AND price < :max_p",
               {'min_s': min_stock, 'max_p': max_price})
products_data = cursor.fetchall()

5.2 执行查询与获取结果

执行查询的流程与普通 SELECT 相同:

  1. cursor.execute(sql_query, parameters): 执行查询。
  2. cursor.fetchone(), cursor.fetchmany(size), cursor.fetchall() 或直接迭代 cursor: 获取结果。

5.3 处理 Python 数据类型

sqlite3 模块会自动将 Python 基本数据类型(str, int, float, bytes, None, bool, datetime.date, datetime.datetime)转换为 SQLite 兼容的格式,并在检索时(如果配置了 detect_types)将其转换回 Python 对象。这使得您可以在 WHERE 子句中直接使用 Python 对象进行比较。

示例:

# 查询特定注册日期后的客户 (datetime.date 对象)
target_date = datetime.date(2023, 3, 1)
cursor.execute("SELECT first_name, last_name, registration_date FROM customers WHERE registration_date >= ?", (target_date,))
recent_customers = cursor.fetchall()

# 查询已完成的订单 (bool 对象)
is_completed_status = True
cursor.execute("SELECT order_id, total_amount, is_completed FROM orders WHERE is_completed = ?", (is_completed_status,))
completed_orders = cursor.fetchall()

6. 性能优化:索引与 WHERE

6.1 索引的重要性

WHERE 子句对表进行过滤时,数据库通常会扫描整个表来查找匹配的行(全表扫描)。对于大型表,这会非常慢。索引可以极大地加速查询,特别是那些在 WHERE 子句中使用的列。索引就像一本书的目录,让数据库可以直接跳转到相关数据,而不是从头到尾翻阅。

6.2 何时创建索引

  • 经常出现在 WHERE 子句中的列
  • 用于 JOIN 条件的列
  • 用于 ORDER BY 子句的列
  • PRIMARY KEYUNIQUE 约束的列会自动创建索引。

然而,索引会增加写入操作(INSERT, UPDATE, DELETE)的开销,因为每次数据更改时,索引也需要更新。因此,需要权衡读取性能和写入性能。

示例 (创建索引):

CREATE INDEX idx_customers_email ON customers (email);
CREATE INDEX idx_orders_customer_id ON orders (customer_id);

7. 错误处理

在使用 WHERE 子句执行查询时,最常见的错误是 sqlite3.OperationalError

7.1 sqlite3.OperationalError

这通常表示 SQL 语法错误、引用的表或列不存在、数据库文件被锁定或损坏等问题。

try:
    # 尝试查询一个不存在的列
    cursor.execute("SELECT non_existent_column FROM customers WHERE customer_id = 1")
    results = cursor.fetchall()
except sqlite3.OperationalError as e:
    print(f"\n捕获到 OperationalError: {e}")
    print("查询失败,请检查列名和表名是否正确。")

8. 最佳实践与注意事项

8.1 再次强调:永远使用参数绑定!

这是最重要的一点。任何时候,只要您在 SQL 查询中包含外部或动态值,就必须使用参数绑定。不要拼接字符串!这不仅防止了 SQL 注入,还处理了复杂的类型转换和转义问题。

8.2 使用 with 语句管理资源

这确保了数据库连接和游标的正确关闭,即使在发生异常时也能自动回滚事务(对于修改数据的操作)。

8.3 理解 NULL 值的特殊性

记住 NULL 永远不等于任何东西(包括另一个 NULL)。因此,总是使用 IS NULLIS NOT NULL 来检查 NULL 值。

8.4 小心使用 LIKEGLOB (性能影响)

  • 前导通配符 (%term*term) 会阻止数据库使用索引,因为它需要扫描所有可能的匹配。如果可能,尽量避免使用前导通配符,或在设计时考虑全文搜索方案。
  • COLLATE NOCASE 可能会在某些情况下影响性能,因为它需要在比较时进行额外的处理。

8.5 优化查询性能:索引、特定列选择

  • WHERE 子句中频繁使用的列创建索引。
  • 只选择您需要的列,而不是 SELECT *
  • 对于大量数据,考虑分页 (LIMIT/OFFSET)。

8.6 外键约束的启用

虽然 WHERE 子句是关于数据过滤而非数据完整性,但在进行 JOIN 操作时,如果外键约束没有启用,您可能会得到不一致的数据或查询结果不符合预期。因此,始终在每个连接中启用 PRAGMA foreign_keys = ON; 是一个好的习惯。

8.7 ALTER TABLE 限制与常见变通方案

(这是针对表结构修改的提醒,与 WHERE 查询本身无关,但作为整个数据库操作的通用最佳实践值得再次提及)SQLite 对 ALTER TABLE 的支持相对有限。例如,它不能直接删除列,也不能直接修改列的数据类型或约束(除了少数例外)。对于更复杂的模式更改(例如,删除列、修改列类型、重新排序列),常见的变通方法是:

  1. 创建具有所需新结构的新表
  2. 将旧表中的数据复制到新表(只复制兼容的列)。
  3. 删除旧表。
  4. 将新表重命名为旧表的名称。
    理解这些限制对于规划数据库模式演进至关重要。

9. 综合代码示例

此示例将基于之前创建的数据库和数据,演示各种 WHERE 子句的实际应用。

import sqlite3
import os
import datetime

db_name = 'comprehensive_where_db.db'
if os.path.exists(db_name):
    os.remove(db_name)

with sqlite3.connect(db_name, detect_types=sqlite3.PARSE_DECLTYPES) as conn:
    conn.row_factory = sqlite3.Row
    cursor = conn.cursor()
    cursor.execute("PRAGMA foreign_keys = ON;")

    # --- 准备数据 (与前几篇深度解析的设置一致) ---
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS customers (
            customer_id INTEGER PRIMARY KEY AUTOINCREMENT,
            first_name TEXT NOT NULL,
            last_name TEXT NOT NULL,
            email TEXT UNIQUE, -- 允许 email 为 NULL,用于演示 IS NULL
            registration_date DATE DEFAULT CURRENT_DATE
        )
    ''')
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS products (
            product_id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT UNIQUE NOT NULL,
            price REAL CHECK(price > 0) DEFAULT 0.0,
            stock INTEGER DEFAULT 0 CHECK(stock >= 0)
        )
    ''')
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS orders (
            order_id INTEGER PRIMARY KEY AUTOINCREMENT,
            customer_id INTEGER NOT NULL,
            order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
            total_amount REAL,
            is_completed BOOLEAN DEFAULT FALSE,
            FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
                ON DELETE CASCADE
        )
    ''')

    # 插入一些示例数据
    cursor.executemany("INSERT INTO customers (first_name, last_name, email, registration_date) VALUES (?, ?, ?, ?)", [
        ("Alice", "Smith", "alice@example.com", datetime.date(2023, 1, 10)),
        ("Bob", "Johnson", "bob@example.com", datetime.date(2023, 1, 15)),
        ("Charlie", "Brown", "charlie@example.com", datetime.date(2023, 2, 20)),
        ("David", "Lee", "david@example.com", datetime.date(2023, 3, 5)),
        ("Eve", "Wang", "eve@example.com", datetime.date(2023, 3, 10)),
        ("Frank", "Green", None, datetime.date(2023, 4, 1)) # Frank 的 email 为 NULL
    ])
    cursor.executemany("INSERT INTO products (name, price, stock) VALUES (?, ?, ?)", [
        ("Laptop", 1200.50, 20),
        ("Mouse", 25.00, 150),
        ("Keyboard", 75.99, 80),
        ("Monitor", 300.00, 30),
        ("Webcam", 49.99, 100),
        ("Desk Lamp", 35.00, 60),
        ("USB Hub", 15.00, 200)
    ])
    
    cursor.execute("SELECT customer_id FROM customers WHERE email = 'alice@example.com'")
    alice_id = cursor.fetchone()['customer_id']
    cursor.execute("SELECT customer_id FROM customers WHERE email = 'bob@example.com'")
    bob_id = cursor.fetchone()['customer_id']
    cursor.execute("SELECT customer_id FROM customers WHERE email = 'charlie@example.com'")
    charlie_id = cursor.fetchone()['customer_id']
    cursor.execute("SELECT customer_id FROM customers WHERE email IS NULL")
    frank_id = cursor.fetchone()['customer_id'] # 获取 Frank 的 ID

    cursor.executemany("INSERT INTO orders (customer_id, order_date, total_amount, is_completed) VALUES (?, ?, ?, ?)", [
        (alice_id, datetime.datetime(2023, 1, 20, 10, 30), 1250.00, False),
        (bob_id, datetime.datetime(2023, 3, 1, 14, 0), 50.00, True),
        (alice_id, datetime.datetime(2023, 2, 5, 9, 0), 200.00, True),
        (charlie_id, datetime.datetime(2023, 4, 10, 11, 45), 800.00, False),
        (bob_id, datetime.datetime(2023, 3, 10, 16, 20), 15.00, True),
        (alice_id, datetime.datetime(2023, 4, 2, 13, 0), 300.00, False),
        (frank_id, datetime.datetime(2023, 4, 5, 10, 0), 99.99, False) # Frank 的订单
    ])
    print("数据库和初始数据已准备好。\n")

    # --- 演示各种 WHERE 子句查询 ---

    print("--- 1. 比较运算符 (=, <) ---")
    # 查询价格大于等于 100 的产品
    min_price_param = 100.0
    cursor.execute("SELECT name, price FROM products WHERE price >= ?", (min_price_param,))
    print(f"  价格 >= ${min_price_param} 的产品:")
    for row in cursor.fetchall():
        print(f"    {row['name']} (价格: ${row['price']})")

    print("\n--- 2. 逻辑运算符 (AND, OR) ---")
    # 查询库存大于 50 且价格小于 200 的产品
    min_stock_param = 50
    max_price_param = 200.0
    cursor.execute("SELECT name, stock, price FROM products WHERE stock > ? AND price < ?", (min_stock_param, max_price_param))
    print(f"  库存 > {min_stock_param} 且价格 < ${max_price_param} 的产品:")
    for row in cursor.fetchall():
        print(f"    {row['name']} (库存: {row['stock']}, 价格: ${row['price']})")

    print("\n--- 3. 范围运算符 (BETWEEN) ---")
    # 查询总金额在 50 到 500 之间的订单
    min_amount_param = 50.0
    max_amount_param = 500.0
    cursor.execute("SELECT order_id, total_amount FROM orders WHERE total_amount BETWEEN ? AND ?", (min_amount_param, max_amount_param))
    print(f"  总金额在 ${min_amount_param} 到 ${max_amount_param} 之间的订单:")
    for row in cursor.fetchall():
        print(f"    订单ID: {row['order_id']}, 总金额: ${row['total_amount']}")

    print("\n--- 4. 集合运算符 (IN, NOT IN) ---")
    # 查询 Alice 和 Bob 的订单
    customer_ids_param = (alice_id, bob_id)
    cursor.execute("SELECT order_id, customer_id, total_amount FROM orders WHERE customer_id IN (?, ?)", customer_ids_param)
    print(f"  Alice (ID:{alice_id}) 和 Bob (ID:{bob_id}) 的订单:")
    for row in cursor.fetchall():
        print(f"    订单ID: {row['order_id']}, 客户ID: {row['customer_id']}, 金额: ${row['total_amount']}")

    print("\n--- 5. 模式匹配 (LIKE, GLOB) ---")
    # 使用 LIKE 查询以 'M' 开头的产品
    like_pattern = 'M%'
    cursor.execute("SELECT name FROM products WHERE name LIKE ?", (like_pattern,))
    print(f"  名称以 '{like_pattern}' 开头的产品 (LIKE):")
    for row in cursor.fetchall():
        print(f"    {row['name']}")

    # 使用 GLOB 查询以 'M' 开头的产品 (GLOB 默认区分大小写)
    glob_pattern = 'M*'
    cursor.execute("SELECT name FROM products WHERE name GLOB ?", (glob_pattern,))
    print(f"  名称以 '{glob_pattern}' 开头的产品 (GLOB):")
    for row in cursor.fetchall():
        print(f"    {row['name']}")
    
    # 使用 GLOB + COLLATE NOCASE 查询以 'm' 开头的产品 (不区分大小写)
    glob_pattern_nocase = 'm*'
    cursor.execute("SELECT name FROM products WHERE name GLOB ? COLLATE NOCASE", (glob_pattern_nocase,))
    print(f"  名称以 '{glob_pattern_nocase}' 开头的产品 (GLOB COLLATE NOCASE):")
    for row in cursor.fetchall():
        print(f"    {row['name']}")

    print("\n--- 6. 空值判断 (IS NULL, IS NOT NULL) ---")
    cursor.execute("SELECT first_name, last_name, email FROM customers WHERE email IS NULL")
    print("  邮箱为 NULL 的客户:")
    for row in cursor.fetchall():
        print(f"    {row['first_name']} {row['last_name']}")

    cursor.execute("SELECT first_name, last_name, email FROM customers WHERE email IS NOT NULL")
    print("\n  邮箱不为 NULL 的客户:")
    for row in cursor.fetchall():
        print(f"    {row['first_name']} {row['last_name']} ({row['email']})")
    
    print("\n--- 7. 存在性判断 (EXISTS) ---")
    cursor.execute('''
        SELECT c.first_name, c.last_name
        FROM customers c
        WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.total_amount > ?)
    ''', (500,))
    print("  下过金额大于 $500 订单的客户:")
    for row in cursor.fetchall():
        print(f"    {row['first_name']} {row['last_name']}")

    print("\n--- 8. 结合多个子句 (WHERE, ORDER BY, LIMIT) ---")
    cursor.execute('''
        SELECT first_name, last_name, registration_date
        FROM customers
        WHERE registration_date >= ?
        ORDER BY registration_date DESC
        LIMIT ?
    ''', (datetime.date(2023, 2, 1), 3))
    print("  2023年2月1日后注册,按注册日期降序排列的前3位客户:")
    for row in cursor.fetchall():
        print(f"    {row['first_name']} {row['last_name']} (注册日期: {row['registration_date']})")

    print("\n--- 9. 结合 JOIN 和 WHERE ---")
    cursor.execute('''
        SELECT c.first_name, c.last_name, o.order_id, o.total_amount, o.is_completed
        FROM customers AS c
        INNER JOIN orders AS o ON c.customer_id = o.customer_id
        WHERE c.first_name = ? AND o.is_completed = ?
    ''', ('Alice', False))
    print("  Alice 未完成的订单:")
    for row in cursor.fetchall():
        print(f"    订单ID: {row['order_id']}, 总金额: ${row['total_amount']}, 完成: {row['is_completed']}")

print(f"\n所有 `WHERE` 子句演示完成,数据库 '{db_name}' 已更新。")

10. 总结

为您详细介绍了 Python SQLite 中 WHERE 子句的各个方面。

核心要点回顾:

  • WHERE 子句是 SQL 查询中必不可少的部分,用于筛选满足特定条件的行。
  • 掌握了各种 WHERE 运算符,包括比较 (=, !=, <, >), 逻辑 (AND, OR, NOT), 范围 (BETWEEN), 集合 (IN, NOT IN), 模式匹配 (LIKE % _, GLOB * ? []), 空值判断 (IS NULL, IS NOT NULL) 和存在性判断 (EXISTS, NOT EXISTS)。
  • 理解 LIKEGLOB 在通配符和大小写敏感性上的区别,并学会使用 COLLATE NOCASE
  • WHERE 子句可以与 ORDER BY, LIMIT/OFFSET, JOIN 等子句结合,构建强大的查询。
  • 在 Python 中,始终使用参数绑定 (?:name) 来传递 WHERE 子句中的值,这是防止 SQL 注入和确保正确数据类型转换的关键。
  • 了解索引在优化带 WHERE 子句的查询性能中的重要性。
  • 遵循最佳实践,如使用 with 语句管理连接、只选择必需的列,并注意 NULL 值的特殊处理。

通过这些全面而深入的知识,您将能够精确地控制从 SQLite 数据库中检索的数据,并构建更安全、高效和健壮的 Python 数据库应用程序。


清理测试文件 (为确保后续测试环境干净)

import os
files_to_clean = [
    'comprehensive_where_db.db'
]
for f in files_to_clean:
    if os.path.exists(f):
        os.remove(f)
print("\n所有测试数据库文件已清理。")

更多推荐