You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PHP/HTML关联展示MySQL两张表数据的问题求助

问题描述
  • 现有两张MySQL表:ho_shifts(存储唯一班次信息)、ho_incidents(存储事件信息,一个班次可关联多个事件,两表通过timenow字段关联)
  • 需求:用PHP实现每个班次仅展示一次,下方汇总该班次对应的所有关联事件,且数据随班次、事件的新增动态更新
  • 当前问题:通过联查SQL获取数据后,每个事件对应一个班次表格,导致班次重复显示;尝试嵌套循环未实现预期效果

表结构示例

ho_shifts 表

CREATE TABLE ho_shifts (
    timenow DATETIME PRIMARY KEY, -- 关联字段
    shift_name VARCHAR(50) NOT NULL,
    shift_start TIME NOT NULL,
    shift_end TIME NOT NULL
    -- 其他班次相关字段
);

ho_incidents 表

CREATE TABLE ho_incidents (
    id INT AUTO_INCREMENT PRIMARY KEY,
    timenow DATETIME NOT NULL, -- 关联班次的字段
    incident_title VARCHAR(100) NOT NULL,
    incident_detail TEXT,
    FOREIGN KEY (timenow) REFERENCES ho_shifts(timenow)
    -- 其他事件相关字段
);

当前错误代码示例

<?php
$conn = mysqli_connect("localhost", "user", "pass", "db");
// 联查但未做分组处理的SQL
$sql = "SELECT s.*, i.* FROM ho_shifts s LEFT JOIN ho_incidents i ON s.timenow = i.timenow";
$result = mysqli_query($conn, $sql);

// 循环输出时,每个事件对应一个班次表格,导致班次重复
while ($row = mysqli_fetch_assoc($result)) {
    echo "<div class='shift-box'>";
    echo "<h3>班次:{$row['shift_name']} | {$row['timenow']}</h3>";
    echo "<p>事件:{$row['incident_title']}</p>";
    echo "</div>";
}
?>

解决方案

推荐两种实现思路,优先选方法1(减少数据库请求,性能更优):

方法1:PHP端数据分组(一次查询,分组处理)

先一次性获取所有联查数据,在PHP中按timenow对班次分组,将同一班次的事件归集后再输出:

<?php
$conn = mysqli_connect("localhost", "user", "pass", "db");

// 联查并排序,确保班次、事件顺序统一
$sql = "SELECT s.*, i.id AS incident_id, i.incident_title, i.incident_detail
        FROM ho_shifts s 
        LEFT JOIN ho_incidents i ON s.timenow = i.timenow 
        ORDER BY s.timenow DESC, i.id ASC";
$result = mysqli_query($conn, $sql);

// 初始化分组数组,key为timenow,value存储班次信息+事件列表
$shiftGroups = [];
while ($row = mysqli_fetch_assoc($result)) {
    $timenow = $row['timenow'];
    // 首次遇到该班次,初始化班次基础信息
    if (!isset($shiftGroups[$timenow])) {
        $shiftGroups[$timenow] = [
            'timenow' => $row['timenow'],
            'shift_name' => $row['shift_name'],
            'shift_start' => $row['shift_start'],
            'shift_end' => $row['shift_end'],
            'incidents' => []
        ];
    }
    // 若存在事件,加入该班次的事件列表
    if (!empty($row['incident_id'])) {
        $shiftGroups[$timenow]['incidents'][] = [
            'id' => $row['incident_id'],
            'title' => $row['incident_title'],
            'detail' => $row['incident_detail']
        ];
    }
}

// 循环输出每个班次及对应事件
foreach ($shiftGroups as $shift) {
    echo "<div class='shift-container'>";
    // 输出班次表格
    echo "<table border='1'>
            <tr>
                <th>班次时间</th>
                <th>班次名称</th>
                <th>开始时间</th>
                <th>结束时间</th>
            </tr>
            <tr>
                <td>{$shift['timenow']}</td>
                <td>{$shift['shift_name']}</td>
                <td>{$shift['shift_start']}</td>
                <td>{$shift['shift_end']}</td>
            </tr>
          </table>";
    
    // 输出事件列表
    echo "<div class='incidents-wrap'>";
    if (!empty($shift['incidents'])) {
        echo "<h4>关联事件</h4>";
        echo "<ul>";
        foreach ($shift['incidents'] as $incident) {
            echo "<li>
                    <strong>{$incident['title']}</strong>
                    <p>{$incident['detail']}</p>
                  </li>";
        }
        echo "</ul>";
    } else {
        echo "<p>无关联事件</p>";
    }
    echo "</div>";
    echo "</div><br>";
}

mysqli_close($conn);
?>

方法2:先查班次再查对应事件(逻辑直观,适合小数据量)

先查询所有班次,再针对每个班次查询关联事件,嵌套循环输出:

<?php
$conn = mysqli_connect("localhost", "user", "pass", "db");

// 先获取所有班次
$shiftSql = "SELECT * FROM ho_shifts ORDER BY timenow DESC";
$shiftResult = mysqli_query($conn, $shiftSql);

while ($shift = mysqli_fetch_assoc($shiftResult)) {
    $timenow = $shift['timenow'];
    // 输出班次信息
    echo "<div class='shift-container'>";
    echo "<table border='1'>
            <tr>
                <th>班次时间</th>
                <th>班次名称</th>
                <th>开始时间</th>
                <th>结束时间</th>
            </tr>
            <tr>
                <td>{$timenow}</td>
                <td>{$shift['shift_name']}</td>
                <td>{$shift['shift_start']}</td>
                <td>{$shift['shift_end']}</td>
            </tr>
          </table>";
    
    // 查询当前班次的事件(用预处理语句防SQL注入)
    $incidentSql = "SELECT * FROM ho_incidents WHERE timenow = ? ORDER BY id ASC";
    $stmt = mysqli_prepare($conn, $incidentSql);
    mysqli_stmt_bind_param($stmt, "s", $timenow);
    mysqli_stmt_execute($stmt);
    $incidentResult = mysqli_stmt_get_result($stmt);
    
    // 输出事件列表
    echo "<div class='incidents-wrap'>";
    if (mysqli_num_rows($incidentResult) > 0) {
        echo "<h4>关联事件</h4>";
        echo "<ul>";
        while ($incident = mysqli_fetch_assoc($incidentResult)) {
            echo "<li>
                    <strong>{$incident['incident_title']}</strong>
                    <p>{$incident['incident_detail']}</p>
                  </li>";
        }
        echo "</ul>";
    } else {
        echo "<p>无关联事件</p>";
    }
    echo "</div>";
    echo "</div><br>";
    
    mysqli_stmt_close($stmt);
}

mysqli_close($conn);
?>

关键提示
  • 方法1仅需一次数据库查询,性能更优,适合数据量大的场景;方法2逻辑简单,但每个班次都会发起一次查询,数据量大时需谨慎使用
  • 务必对SQL查询结果排序(如ORDER BY s.timenow DESC),保证输出顺序符合需求
  • 涉及用户输入的查询,必须使用预处理语句防止SQL注入

内容的提问来源于stack exchange,提问作者SmoothBrain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:05:15