PHP MySQL Where

在PHP MySQL查询中,WHERE子句用于过滤记录,只返回满足指定条件的记录。本教程将介绍如何在PHP中使用WHERE子句进行条件查询,包括各种操作符、逻辑运算符以及常见的条件过滤方法。

基本的WHERE子句

WHERE子句用于在SELECT、UPDATE或DELETE语句中过滤记录。

1. MySQLi面向对象方式

<?php
// 数据库连接参数
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);

// 检查连接
if ($conn->connect_error) {
    die("连接失败: " . $conn->connect_error);
}

// SQL查询语句 - 带有WHERE子句
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE lastname='Doe'";
$result = $conn->query($sql);

// 检查结果集
if ($result->num_rows > 0) {
    // 输出每行数据
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 名: " . $row["firstname"] . " - 姓: " . $row["lastname"] . "<br>";
    }
} else {
    echo "0 结果";
}

// 关闭连接
$conn->close();
?>

2. MySQLi过程式方式

<?php
// 数据库连接参数
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// 创建连接
$conn = mysqli_connect($servername, $username, $password, $dbname);

// 检查连接
if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

// SQL查询语句 - 带有WHERE子句
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE lastname='Doe'";
$result = mysqli_query($conn, $sql);

// 检查结果集
if (mysqli_num_rows($result) > 0) {
    // 输出每行数据
    while (row = mysqli_fetch_assoc($result)) {
        echo "ID: " . $row["id"] . " - 名: " . $row["firstname"] . " - 姓: " . $row["lastname"] . "<br>";
    }
} else {
    echo "0 结果";
}

// 关闭连接
mysqli_close($conn);
?>

3. 使用PDO

<?php
// 数据库连接参数
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// 尝试连接数据库
try {
    $conn = new PDO("mysql:host=$servername;dbname=$dbname;charset=utf8mb4", $username, $password);
    
    // 设置PDO错误模式为异常
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    // SQL查询语句 - 带有WHERE子句
    $sql = "SELECT id, firstname, lastname FROM MyGuests WHERE lastname='Doe'";
    
    // 准备和执行查询
    $stmt = $conn->query($sql);
    
    // 设置获取模式为关联数组
    $stmt->setFetchMode(PDO::FETCH_ASSOC);
    
    // 获取所有结果
    $results = $stmt->fetchAll();
    
    // 输出结果
    if (count($results) > 0) {
        foreach ($results as row) {
            echo "ID: " . $row["id"] . " - 名: " . $row["firstname"] . " - 姓: " . $row["lastname"] . "<br>";
        }
    } else {
        echo "0 结果";
    }
} catch(PDOException $e) {
    echo "错误: " . $e->getMessage();
}

// 关闭连接
$conn = null;
?>

WHERE子句中的比较运算符

MySQL支持多种比较运算符,可以在WHERE子句中使用:

运算符 描述 示例
= 等于 WHERE id = 5
<> 或 != 不等于 WHERE category != 'Books'
> 大于 WHERE price > 100
< 小于 WHERE quantity < 50
>= 大于或等于 WHERE age >= 18
<= 小于或等于 WHERE discount <= 0.2
BETWEEN 在某个范围内 WHERE price BETWEEN 50 AND 100
LIKE 搜索模式 WHERE name LIKE 'A%'
IN 指定多个可能的值 WHERE color IN ('Red', 'Green', 'Blue')
IS NULL 值为NULL WHERE description IS NULL
IS NOT NULL 值不为NULL WHERE description IS NOT NULL

比较运算符示例

1. 使用大于(>)运算符

<?php
// 数据库连接代码...

// 查询年龄大于30的用户
$sql = "SELECT id, firstname, lastname, age FROM users WHERE age > 30";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . " - 年龄: " . $row["age"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

2. 使用BETWEEN运算符

<?php
// 数据库连接代码...

// 查询价格在50到100之间的产品
$sql = "SELECT id, product_name, price FROM products WHERE price BETWEEN 50 AND 100";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . " - 价格: $" . $row["price"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

3. 使用LIKE运算符进行模糊查询

<?php
// 数据库连接代码...

// 查询名称以"A"开头的产品
$sql = "SELECT id, product_name FROM products WHERE product_name LIKE 'A%'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . "<br>";
    }
} else {
    echo "0 结果";
}

// 查询名称包含"book"的产品(不区分大小写)
$sql = "SELECT id, product_name FROM products WHERE product_name LIKE '%book%'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

4. 使用IN运算符

<?php
// 数据库连接代码...

// 查询类别为Books, Electronics或Clothing的产品
$sql = "SELECT id, product_name, category FROM products WHERE category IN ('Books', 'Electronics', 'Clothing')";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . " - 类别: " . $row["category"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

5. 处理NULL值

<?php
// 数据库连接代码...

// 查询描述为NULL的产品
$sql = "SELECT id, product_name FROM products WHERE description IS NULL";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . "<br>";
    }
} else {
    echo "0 结果";
}

// 查询描述不为NULL的产品
$sql = "SELECT id, product_name FROM products WHERE description IS NOT NULL";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

逻辑运算符

MySQL支持以下逻辑运算符,用于组合多个条件:

运算符 描述 示例
AND 当所有条件都为真时返回记录 WHERE price > 50 AND category = 'Books'
OR 当任一条件为真时返回记录 WHERE category = 'Books' OR category = 'Electronics'
NOT 反转条件的结果 WHERE NOT category = 'Clothing'

逻辑运算符示例

1. 使用AND运算符

<?php
// 数据库连接代码...

// 查询价格大于50且类别为Books的产品
$sql = "SELECT id, product_name, price, category FROM products WHERE price > 50 AND category = 'Books'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . " - 价格: $" . $row["price"] . " - 类别: " . $row["category"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

2. 使用OR运算符

<?php
// 数据库连接代码...

// 查询类别为Books或Electronics的产品
$sql = "SELECT id, product_name, category FROM products WHERE category = 'Books' OR category = 'Electronics'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . " - 类别: " . $row["category"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

3. 组合使用AND和OR运算符

<?php
// 数据库连接代码...

// 查询类别为Books且价格大于50,或者类别为Electronics的产品
$sql = "SELECT id, product_name, price, category FROM products WHERE (category = 'Books' AND price > 50) OR category = 'Electronics'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 产品: " . $row["product_name"] . " - 价格: $" . $row["price"] . " - 类别: " . $row["category"] . "<br>";
    }
} else {
    echo "0 结果";
}
?>

使用预处理语句防止SQL注入

当WHERE子句中包含用户输入时,应始终使用预处理语句来防止SQL注入攻击。

1. MySQLi预处理语句

<?php
// 数据库连接代码...

// 假设这是从表单或URL获取的用户输入
$lastname = $_GET['lastname'] ?? '';
$age_min = $_GET['age_min'] ?? '0';

// 使用预处理语句
$stmt = $conn->prepare("SELECT id, firstname, lastname, age FROM users WHERE lastname = ? AND age >= ?");
$stmt->bind_param("si", $lastname, $age_min); // s = string, i = integer

// 执行查询
$stmt->execute();

// 获取结果
$result = $stmt->get_result();

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . " - 年龄: " . $row["age"] . "<br>";
    }
} else {
    echo "0 结果";
}

// 关闭语句
$stmt->close();
?>

2. PDO预处理语句

<?php
// 数据库连接代码...

// 假设这是从表单或URL获取的用户输入
$lastname = $_GET['lastname'] ?? '';
$age_min = $_GET['age_min'] ?? '0';

// 使用PDO预处理语句
$stmt = $conn->prepare("SELECT id, firstname, lastname, age FROM users WHERE lastname = :lastname AND age >= :age_min");

// 绑定参数
$stmt->bindParam(':lastname', $lastname, PDO::PARAM_STR);
$stmt->bindParam(':age_min', $age_min, PDO::PARAM_INT);

// 或者直接在execute中传递参数数组
// $stmt->execute([':lastname' => $lastname, ':age_min' => $age_min]);

// 执行查询
$stmt->execute();

// 设置获取模式
$stmt->setFetchMode(PDO::FETCH_ASSOC);

// 获取结果
while (row = $stmt->fetch()) {
    echo "ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . " - 年龄: " . $row["age"] . "<br>";
}

// 检查是否有结果
if ($stmt->rowCount() == 0) {
    echo "0 结果";
}
?>

处理日期和时间条件

MySQL提供了多种函数来处理日期和时间条件。

<?php
// 数据库连接代码...

// 查询在特定日期之后注册的用户
$sql = "SELECT id, firstname, lastname, reg_date FROM users WHERE reg_date > '2023-01-01'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . " - 注册日期: " . $row["reg_date"] . "<br>";
    }
}

// 使用NOW()函数查询今天注册的用户
$sql = "SELECT id, firstname, lastname, reg_date FROM users WHERE DATE(reg_date) = DATE(NOW())";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . " - 注册日期: " . $row["reg_date"] . "<br>";
    }
}

// 查询过去7天内注册的用户
$sql = "SELECT id, firstname, lastname, reg_date FROM users WHERE reg_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . " - 注册日期: " . $row["reg_date"] . "<br>";
    }
}
?>

子查询

子查询是嵌套在另一个SQL查询中的查询。在WHERE子句中使用子查询可以基于另一个查询的结果进行过滤。

<?php
// 数据库连接代码...

// 查询订单总金额大于平均订单金额的订单
$sql = "SELECT order_id, customer_id, total_amount FROM orders WHERE total_amount > (SELECT AVG(total_amount) FROM orders)";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "订单ID: " . $row["order_id"] . " - 客户ID: " . $row["customer_id"] . " - 总金额: $" . $row["total_amount"] . "<br>";
    }
}

// 查询购买了特定产品的客户
$sql = "SELECT id, firstname, lastname FROM customers WHERE id IN (SELECT DISTINCT customer_id FROM orders WHERE order_id IN (SELECT order_id FROM order_items WHERE product_id = 10))";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_assoc()) {
        echo "客户ID: " . $row["id"] . " - 姓名: " . $row["firstname"] . " " . $row["lastname"] . "<br>";
    }
}
?>

WHERE子句最佳实践

  • 始终使用预处理语句:当WHERE子句中包含用户输入时,使用预处理语句防止SQL注入
  • 使用适当的索引:在WHERE子句中频繁使用的列上创建索引,以提高查询性能
  • 避免在WHERE子句中对列进行函数操作:这会导致索引失效,例如:WHERE DATE(reg_date) = '2023-01-01'
  • 使用合适的比较运算符:根据数据类型和查询需求选择合适的运算符
  • 注意NULL值的处理:NULL值需要使用IS NULL或IS NOT NULL来检查,不能使用=或!=
  • 使用括号明确逻辑顺序:当组合多个AND和OR条件时,使用括号明确逻辑顺序
  • 优化LIKE查询:避免以通配符开头的LIKE查询,如LIKE '%keyword%',这会导致全表扫描
  • 对于大范围筛选使用BETWEEN:当筛选范围值时,使用BETWEEN通常比使用>和<组合更高效
  • 使用EXPLAIN分析查询性能:使用EXPLAIN语句分析WHERE子句的执行计划
  • 限制返回的列数:只选择需要的列,而不是使用SELECT *

常见陷阱和错误

  • 忘记处理SQL注入:直接将用户输入拼接到SQL查询中是非常危险的
  • 错误处理NULL值:使用= NULL而不是IS NULL来检查NULL值
  • 数据类型不匹配:比较不同数据类型的值时可能会导致意外结果
  • 忽略大小写敏感性:MySQL默认情况下,字符串比较是不区分大小写的,但这取决于所使用的字符集和排序规则
  • 逻辑运算符顺序错误:忘记使用括号导致逻辑顺序错误
  • 过度使用OR条件:大量的OR条件可能导致查询性能下降,可以考虑使用IN运算符替代