PHP MySQL 读取数据

读取数据库中的数据是PHP MySQL操作中最常见的任务之一。本教程将介绍如何使用PHP从MySQL数据库中读取数据,包括MySQLi扩展和PDO(PHP数据对象)两种方式,以及不同的结果获取方法。

使用MySQLi读取数据

MySQLi扩展提供了面向对象和过程式两种方式来读取数据。

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查询语句
$sql = "SELECT id, firstname, lastname FROM MyGuests";
$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查询语句
$sql = "SELECT id, firstname, lastname FROM MyGuests";
$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);
?>

MySQLi的不同结果获取方式

MySQLi提供了多种获取结果的方法:

  • fetch_assoc() - 以关联数组形式获取结果
  • fetch_row() - 以索引数组形式获取结果
  • fetch_array() - 以关联数组和索引数组两种形式获取结果
  • fetch_object() - 以对象形式获取结果
<?php
// 数据库连接代码...

// 1. fetch_assoc() - 关联数组
$sql = "SELECT id, firstname, lastname FROM MyGuests";
$result = $conn->query($sql);

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

// 2. fetch_row() - 索引数组
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_row()) {
        echo "ID: " . $row[0] . ", 名: " . $row[1] . "<br>";
    }
}

// 3. fetch_array() - 关联数组和索引数组
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_array()) {
        echo "ID: " . $row[0] . " 或 " . $row["id"] . ", 名: " . $row[1] . " 或 " . $row["firstname"] . "<br>";
    }
}

// 4. fetch_object() - 对象
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while (row = $result->fetch_object()) {
        echo "ID: " . $row->id . ", 名: " . $row->firstname . "<br>";
    }
}
?>

使用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);
}

// 准备预处理语句
$stmt = $conn->prepare("SELECT id, firstname, lastname FROM MyGuests WHERE lastname = ?");
$stmt->bind_param("s", $lastname);

// 设置参数
$lastname = "Doe";

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

// 绑定结果变量
$stmt->bind_result($id, $firstname, $lastname);

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

// 关闭语句和连接
$stmt->close();
$conn->close();
?>

使用PDO读取数据

PDO(PHP数据对象)提供了一种与数据库类型无关的方式来读取数据。

<?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查询语句
    $sql = "SELECT id, firstname, lastname FROM MyGuests";
    
    // 准备和执行查询
    $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;
?>

PDO的不同获取模式

PDO提供了多种获取结果的模式:

  • PDO::FETCH_ASSOC - 以关联数组形式获取结果
  • PDO::FETCH_NUM - 以索引数组形式获取结果
  • PDO::FETCH_BOTH - 以关联数组和索引数组两种形式获取结果(默认)
  • PDO::FETCH_OBJ - 以对象形式获取结果
  • PDO::FETCH_CLASS - 以指定类的实例形式获取结果
  • PDO::FETCH_COLUMN - 只获取指定的列
<?php
// 数据库连接代码...

// SQL查询语句
$sql = "SELECT id, firstname, lastname FROM MyGuests";
$stmt = $conn->query($sql);

// 1. PDO::FETCH_ASSOC - 关联数组
$stmt->setFetchMode(PDO::FETCH_ASSOC);
while (row = $stmt->fetch()) {
    echo "ID: " . $row["id"] . ", 名: " . $row["firstname"] . "<br>";
}

// 重新执行查询
$stmt = $conn->query($sql);

// 2. PDO::FETCH_NUM - 索引数组
$stmt->setFetchMode(PDO::FETCH_NUM);
while (row = $stmt->fetch()) {
    echo "ID: " . $row[0] . ", 名: " . $row[1] . "<br>";
}

// 重新执行查询
$stmt = $conn->query($sql);

// 3. PDO::FETCH_OBJ - 对象
$stmt->setFetchMode(PDO::FETCH_OBJ);
while (row = $stmt->fetch()) {
    echo "ID: " . $row->id . ", 名: " . $row->firstname . "<br>";
}

// 4. PDO::FETCH_CLASS - 类实例
class Guest {
    public $id;
    public $firstname;
    public $lastname;
}

$stmt = $conn->query($sql);
$guests = $stmt->fetchAll(PDO::FETCH_CLASS, 'Guest');

foreach ($guests as guest) {
    echo "ID: " . $guest->id . ", 名: " . $guest->firstname . "<br>";
}

// 5. PDO::FETCH_COLUMN - 单列
$stmt = $conn->query($sql);
$firstnames = $stmt->fetchAll(PDO::FETCH_COLUMN, 1); // 1 是firstname列的索引

foreach ($firstnames as firstname) {
    echo "名: " . $firstname . "<br>";
}
?>

使用PDO预处理语句读取数据

使用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);
    
    // 准备预处理语句
    $stmt = $conn->prepare("SELECT id, firstname, lastname FROM MyGuests WHERE lastname = :lastname");
    
    // 执行查询
    $stmt->execute([
        ':lastname' => "Doe"
    ]);
    
    // 设置获取模式
    $stmt->setFetchMode(PDO::FETCH_ASSOC);
    
    // 获取结果
    while (row = $stmt->fetch()) {
        echo "ID: " . $row['id'] . " - 名: " . $row['firstname'] . " - 姓: " . $row['lastname'] . "<br>";
    }
} catch(PDOException $e) {
    echo "错误: " . $e->getMessage();
}

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

读取数据并显示在HTML表格中

在实际应用中,我们经常需要将查询结果以表格形式展示给用户。

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

// SQL查询语句
$sql = "SELECT id, firstname, lastname, email, reg_date FROM MyGuests";
$result = $conn->query($sql);
?>

<!DOCTYPE html>
<html>
<head>
<style>
/* 简单的表格样式 */
table {
    width: 100%;
    border-collapse: collapse;
}
table, th, td {
    border: 1px solid black;
}
th, td {
    padding: 10px;
    text-align: left;
}
th {
    background-color: #f2f2f2;
}
tr:nth-child(even) {
    background-color: #f9f9f9;
}
</style>
</head>
<body>

<h2>用户列表</h2>

<table>
    <tr>
        <th>ID</th>
        <th>名</th>
        <th>姓</th>
        <th>邮箱</th>
        <th>注册日期</th>
    </tr>
    <?php
    if ($result->num_rows > 0) {
        // 输出每行数据
        while (row = $result->fetch_assoc()) {
            echo "<tr><td>" . $row["id"] . "</td><td>" . $row["firstname"] . "</td><td>" . $row["lastname"] . "</td><td>" . $row["email"] . "</td><td>" . $row["reg_date"] . "</td></tr>";
        }
    } else {
        echo "<tr><td colspan='5'>0 结果</td></tr>";
    }
    $conn->close();
    >
</table>

</body>
</html>

分页读取数据

当数据量很大时,通常需要实现分页功能。

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

// 配置分页参数
$records_per_page = 10; // 每页显示的记录数

// 获取当前页码,如果没有设置则默认为1
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$page = $page < 1 ? 1 : $page; // 确保页码不小于1

// 计算偏移量
$offset = ($page - 1) * $records_per_page;

// 1. 获取总记录数
$total_sql = "SELECT COUNT(*) as total FROM MyGuests";
$total_result = $conn->query($total_sql);
$total_row = $total_result->fetch_assoc();
$total_records = $total_row['total'];

// 计算总页数
$total_pages = ceil($total_records / $records_per_page);

// 2. 获取当前页的数据
$sql = "SELECT id, firstname, lastname, email FROM MyGuests LIMIT ? OFFSET ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ii", $records_per_page, $offset);
$stmt->execute();
$result = $stmt->get_result();

// 显示数据
if ($result->num_rows > 0) {
    echo "<table><tr><th>ID</th><th>名</th><th>姓</th><th>邮箱</th></tr>";
    
    while (row = $result->fetch_assoc()) {
        echo "<tr><td>" . $row["id"] . "</td><td>" . $row["firstname"] . "</td><td>" . $row["lastname"] . "</td><td>" . $row["email"] . "</td></tr>";
    }
    
    echo "</table>";
    
    // 显示分页链接
    echo "<div class='pagination'>";
    
    // 上一页链接
    if ($page > 1) {
        echo "<a href='?page=" . ($page - 1) . "'>上一页</a>";
    }
    
    // 页码链接
    for (i = 1; i <= $total_pages; i++) {
        if (i == $page) {
            echo "<a href='?page=" . i . "' class='active'>" . i . "</a>";
        } else {
            echo "<a href='?page=" . i . "'>" . i . "</a>";
        }
    }
    
    // 下一页链接
    if ($page < $total_pages) {
        echo "<a href='?page=" . ($page + 1) . "'>下一页</a>";
    }
    
    echo "</div>";
} else {
    echo "0 结果";
}

// 关闭语句和连接
$stmt->close();
$conn->close();
?>

统计和聚合查询

MySQL提供了多种聚合函数来统计和计算数据。

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

// 1. 计算记录总数
$sql = "SELECT COUNT(*) as total FROM MyGuests";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
echo "总记录数: " . $row['total'] . "<br>";

// 2. 计算平均值
$sql = "SELECT AVG(price) as avg_price FROM products";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
echo "平均价格: " . $row['avg_price'] . "<br>";

// 3. 计算最大值和最小值
$sql = "SELECT MAX(price) as max_price, MIN(price) as min_price FROM products";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
echo "最高价格: " . $row['max_price'] . ", 最低价格: " . $row['min_price'] . "<br>";

// 4. 计算总和
$sql = "SELECT SUM(quantity) as total_quantity FROM order_items";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
echo "总数量: " . $row['total_quantity'] . "<br>";

// 5. 分组统计
$sql = "SELECT category, COUNT(*) as count FROM products GROUP BY category";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    echo "<h3>按分类统计</h3>";
    while (row = $result->fetch_assoc()) {
        echo "分类: " . $row['category'] . ", 数量: " . $row['count'] . "<br>";
    }
}

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

读取大型结果集

当处理大型结果集时,我们需要考虑内存使用问题。

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

// 使用MySQLi的unbuffered查询(减少内存使用)
$sql = "SELECT id, data FROM large_table";
$result = $conn->query($sql, MYSQLI_USE_RESULT);

if ($result) {
    while (row = $result->fetch_assoc()) {
        // 处理每一行数据,但不要将所有数据存储在内存中
        process_row($row);
    }
    $result->close();
}

// 使用PDO的游标(cursor)处理大型结果集
$stmt = $conn->prepare("SELECT id, data FROM large_table", [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
$stmt->execute();

while (row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT)) {
    // 处理每一行数据
    process_row($row);
}
?>

最佳实践

  • 只选择需要的列:不要使用 SELECT *,而是明确指定需要的列,以提高性能
  • 使用预处理语句:当查询包含用户输入时,使用预处理语句防止SQL注入
  • 限制结果集大小:使用LIMIT子句限制返回的记录数,特别是在开发和测试阶段
  • 处理空结果集:始终检查查询是否返回了数据
  • 使用索引:确保查询中使用的列有适当的索引,以提高查询性能
  • 关闭结果集和连接:操作完成后,及时关闭结果集和数据库连接
  • 优化大型结果集:对于大型结果集,考虑使用分页或流式读取
  • 处理错误:使用try-catch块或错误处理函数捕获和处理可能出现的错误
  • 使用缓存:对于不经常变化的数据,考虑使用缓存机制减少数据库查询
  • 监控性能:使用EXPLAIN等工具分析查询性能,找出优化空间