
本文详解如何通过 PHP 预处理数据 + HTML rowspan 属性,将相同“Name Product”的多行记录智能合并,使表格结构更清晰、专业,避免重复显示冗余产品名。
本文详解如何通过 php 预处理数据 + html `rowspan` 属性,将相同“name product”的多行记录智能合并,使表格结构更清晰、专业,避免重复显示冗余产品名。
在实际 Web 开发中,直接逐行输出数据库结果常导致表格视觉混乱——尤其是当同一产品(如 ADA)对应多个 Type(如 B112、B253、K23)时,若每行都重复显示 ADA,既浪费空间又降低可读性。理想的解决方案是:仅首行显示产品名,并用 rowspan 跨越后续同产品行数。
这要求我们摒弃“边查边输出”的惯性思维,转而采用 数据预处理 → 结构分析 → 模板渲染 的三步法,严格遵循“关注点分离”原则。
✅ 正确实现步骤
1. 查询并分组原始数据
使用 GROUP BY \Name Product`, `Type`确保每种产品-类型组合独立成行;同时用GROUP_CONCAT汇总关联的Code Unit`(便于后续按钮传参):
SELECT `Name Product`, `Type`, COUNT(*) AS `NoU`, GROUP_CONCAT(`Code Unit`) AS `Kodeunit` FROM `stock goods` GROUP BY `Name Product`, `Type` ORDER BY `Name Product`, `Type`;
2. PHP 预处理:构建带 rowspan 逻辑的数据结构
核心思想:遍历结果集,统计每个产品名连续出现的行数,并标记哪些行需留空(即非首行):
立即学习“前端免费学习笔记(深入)”;
<?php
$records = [];
$currentProduct = null;
$rowIndex = 0;
while ($row = mysqli_fetch_assoc($result)) {
$productName = $row["Name Product"];
// 检测产品名变更:新组开始
if ($productName !== $currentProduct) {
$currentProduct = $productName;
// 记录该产品首次出现的位置
$records[] = [
'product' => $productName,
'rows' => [],
'rowspan' => 0 // 占位,后续填充
];
$lastGroupIndex = count($records) - 1;
}
// 将当前行加入最新分组
$records[$lastGroupIndex]['rows'][] = [
'no' => ++$rowIndex,
'type' => $row["Type"],
'units' => $row["NoU"] . " Pcs",
'codes' => explode(',', $row["Kodeunit"])
];
}
// 补充 rowspan 值(每组行数)
foreach ($records as &$group) {
$group['rowspan'] = count($group['rows']);
}
?>3. 渲染 HTML 表格(关键:精准控制 <td> 输出)<p>利用 <code>rowspan 合并产品名列,其余列正常输出:<table id="customers" class="s-table">
<thead>
<tr>
<th>No</th>
<th>Name Product</th>
<th>Type</th>
<th>Num of Units</th>
<th>Button Click</th>
</tr>
</thead>
<tbody>
<?php $globalNo = 1; ?>
<?php foreach ($records as $group): ?>
<?php foreach ($group['rows'] as $i => $row): ?>
<tr>
<td><?= $globalNo++ ?></td>
<?php if ($i === 0): ?>
<td rowspan="<?= $group['rowspan'] ?>"><?= htmlspecialchars($group['product']) ?></td>
<?php endif; ?>
<td><?= htmlspecialchars($row['type']) ?></td>
<td><?= htmlspecialchars($row['units']) ?></td>
<td>
<button onclick="myFunction(
<?= json_encode($group['product']) ?>,
<?= json_encode($row['type']) ?>,
<?= json_encode($row['codes']) ?>
)">List Stock</button>
</td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>⚠️ 注意事项与最佳实践
-
安全第一:始终对输出内容调用
htmlspecialchars() 防止 XSS;用 json_encode() 处理 JS 参数,避免引号冲突。
-
rowspan 逻辑验证:确保
rowspan 值 ≥ 1,且不跨 <thead> 或 <code><tbody> 边界。 <li>
<strong>空值处理</strong>:若某产品无 <code>Type 数据,需提前过滤或提供默认占位符(如 "N/A")。
-
性能优化:大数据量时,考虑在 SQL 层用窗口函数(如
ROW_NUMBER() OVER (PARTITION BY ...))辅助分组,减少 PHP 内存压力。
通过以上结构化处理,你将得到高度整洁的表格:ADA 仅在首行显示并纵向跨越 3 行,DUZK 独立占据 2 行,Naro 单独成组——语义清晰、维护性强,真正实现“所见即所需”。
htmlspecialchars() 防止 XSS;用 json_encode() 处理 JS 参数,避免引号冲突。 rowspan 值 ≥ 1,且不跨 <thead> 或 <code><tbody> 边界。 <li>
<strong>空值处理</strong>:若某产品无 <code>Type 数据,需提前过滤或提供默认占位符(如 "N/A")。 ROW_NUMBER() OVER (PARTITION BY ...))辅助分组,减少 PHP 内存压力。 通过以上结构化处理,你将得到高度整洁的表格:ADA 仅在首行显示并纵向跨越 3 行,DUZK 独立占据 2 行,Naro 单独成组——语义清晰、维护性强,真正实现“所见即所需”。



















