
本文介绍一种无需重复编写 26 个 sql 查询的优雅方案:先一次性读取全部歌曲,再通过 php 动态分组、拆分为左右两列,最后渲染为带字母标题的响应式表格结构。
本文介绍一种无需重复编写 26 个 sql 查询的优雅方案:先一次性读取全部歌曲,再通过 php 动态分组、拆分为左右两列,最后渲染为带字母标题的响应式表格结构。
在构建歌单类应用(如在线歌本)时,常需将海量歌曲按首字母分类展示,并均衡分布于左右两栏(如 A–M 在左,N–Z 在右)。若为每个字母单独写一条 LIKE ‘A%’ 查询并重复 26 次——不仅严重违反 DRY 原则,更导致数据库连接开销倍增、维护成本陡升、且无法自动跳过空字母(如无以 X 开头的歌曲时仍保留 X 标题)。
核心思路:一次查询 + 内存分组 + 智能切分
我们摒弃“为每个字母发一次查询”的低效模式,转而采用三步法:
- 单次全量查询:从数据库一次性获取所有歌曲,按 title ASC 排序;
- PHP 端分组聚合:遍历结果,以首字母为键,将歌曲归入关联数组;
- 等分双列布局:将分组后的数组均分为左右两部分,分别渲染。
以下是完整、可直接集成的生产级实现:
立即学习“PHP 免费学习笔记(深入)”;
<?php // 1. 一次性查询全部歌曲(含 ID、标题、和弦等字段)$sql = "SELECT id, title, chord FROM song ORDER BY title ASC"; $stmt = $conn->prepare($sql); $stmt->execute(); $songs = $stmt->fetchAll(PDO::FETCH_ASSOC); // 2. 按首字母分组(自动过滤空格 / 标点,增强鲁棒性)$grouped = []; foreach ($songs as $song) {$firstChar = strtoupper($song['title'][0] ?? ''); // 跳过非字母开头(如数字、符号),确保只分组 A-Z if (ctype_alpha($firstChar)) {$grouped[$firstChar][] = $song;} } // 3. 将分组数组均分为左右两列(保持键名,避免索引丢失)$midpoint = ceil(count($grouped) / 2); $columns = array_chunk($grouped, $midpoint, true); $leftColumn = $columns[0] ?? []; $rightColumn = $columns[1] ?? []; // 4. 定义安全渲染函数(防 XSS,结构清晰)function renderColumn(array $letters, string $sideClass): string {$html = "<div class="column {$sideClass}">n"; $html .= " <div class="table-wrapper">n"; $html .= " <table class="fl-table">n"; foreach ($letters as $letter => $songsByLetter) {// 字母标题行 $html .= " <tr>n"; $html .= " <th style="color:black; width:80%">{$letter}</th>n"; $html .= " <th style="color:white;"></th>n"; $html .= " </tr>n"; // 歌曲数据行 foreach ($songsByLetter as $song) {$id = (int)$song['id']; $title = htmlspecialchars($song['title'], ENT_QUOTES,'UTF-8'); $chord = htmlspecialchars($song['chord'] ??'', ENT_QUOTES, 'UTF-8'); $html .= " <tr>n"; $html .= " <td><a style="margin-left:10px" href="details.php?id={$id}">{$title}</a></td>n"; $html .= " <td><span style="margin-left:10px">{$chord}</span></td>n"; $html .= " </tr>n"; } } $html .= " </table>n"; $html .= " </div>n"; $html .= "</div>n"; return $html; } ?>
前端调用(插入 HTML 主体中):
<div class="song-columns"> <?= renderColumn($leftColumn, 'left') ?> <?= renderColumn($rightColumn, 'right') ?> </div>
✅ 关键优势说明:
- 零 SQL 重复:仅 1 次数据库查询,性能提升显著;
- 自动适应:若某字母无歌曲(如 X、Z),该分组自然为空,不会渲染空白标题;
- 安全可靠:使用 htmlspecialchars() 防止 XSS,ctype_alpha() 过滤非法首字符;
- 结构清晰:逻辑分离(数据获取 → 分组 → 切分 → 渲染),便于单元测试与后续扩展(如支持多语言首字母、拼音排序等);
- 样式解耦:HTML 结构与 CSS 完全分离,可通过 .left / .right 类灵活控制响应式断点(如移动端堆叠、桌面端并排)。
⚠️ 注意事项:
- 若歌曲量极大(>10 万条),建议在 title 字段添加前缀索引(INDEX idx_title_first (title(1)))加速排序;
- 中文歌名需额外处理(如转拼音或提取汉字首字),本方案默认适用于拉丁字母歌名;
- array_chunk(…, true) 的第三个参数 true 至关重要——它保留原始键名(A、B、C……),否则分组后字母索引将丢失。
此方案将重复劳动转化为可复用的抽象逻辑,是 PHP Web 开发中“用数据驱动视图”的典型实践。你只需专注业务数据本身,让代码为你智能组织世界。