PHP+PostgreSQL环境下如何获取设备历史记录的上一任所有者信息
PHP+PostgreSQL环境下如何获取设备历史记录的上一任所有者信息
兄弟,我看你现在的需求是要从设备的历史流转记录里,拿到最后流转到仓库前的上一任所有者对吧?你现在用计数器的思路其实方向是对的,但其实可以从SQL层面或者PHP循环处理两个方向优化,给你理清楚两种好用的方案:
方案一:用PostgreSQL窗口函数直接获取(推荐,效率更高)
PostgreSQL自带的LAG()窗口函数可以直接在SQL查询里拿到同一设备的上一条历史记录的字段值,不用在PHP里绕来绕去做计数器,效率高很多。
先改写你的嵌套查询,直接把上一任所有者查出来:
SELECT main.s_n, employers.employer AS current_owner, history.date AS current_date, -- 核心:拿到同一设备的上一条记录的所有者,也就是上一任 LAG(employers.employer) OVER (PARTITION BY main.s_n ORDER BY history.date) AS prev_owner FROM main INNER JOIN history ON main.device_code = history.device_code INNER JOIN employers ON employers.employer_code = history.employer_code -- 按设备分组、日期排序,保证LAG拿到的是时间上的前一次记录 ORDER BY main.s_n, history.date DESC
关键参数解释:
PARTITION BY main.s_n:按设备序列号分组,确保只在同一设备的历史记录里找上一条ORDER BY history.date:按日期正序排序,这样LAG()就能精准拿到时间上的上一次流转记录
然后PHP里的处理就简化太多了,而且还能避免原来的指针问题:
// 先查所有目标设备的基础信息 $result = pg_query($conn, "SELECT name,model,inventory_code,s_n,device_type_code FROM public.main WHERE device_type_code=1 ORDER BY main.s_n"); if (!$result) { echo "An error occurred.\n"; exit; } // 注意!把整个表格的框架和表头提到循环外面,别每次循环都输出完整表格! echo "<table class='styled-table'> <thead> <tr> <td>Equipment name</td> <td>Model</td> <td>ID</td> <td>Last date</td> <td>Current owner</td> <td>Previous owner</td> </tr> </thead> <tbody>"; // 遍历每个设备 while ($row = pg_fetch_row($result)) { $serial_number = $row[3]; // 用改写后的SQL查该设备的历史记录,只取最新的一条(因为我们要最后流转到仓库的状态) $result2 = pg_query($conn, " SELECT employers.employer AS current_owner, history.date AS current_date, LAG(employers.employer) OVER (PARTITION BY main.s_n ORDER BY history.date) AS prev_owner FROM main INNER JOIN history ON main.device_code = history.device_code INNER JOIN employers ON employers.employer_code = history.employer_code WHERE main.s_n = '$serial_number' ORDER BY history.date DESC LIMIT 1; "); if ($row2 = pg_fetch_assoc($result2)) { $last_date = $row2['current_date']; $last_owner = $row2['current_owner']; // 处理没有上一任的情况(比如设备刚入库,还没被租过) $prev_owner = $row2['prev_owner'] ?? "There were no transfers"; // 只显示最后在仓库的设备 if ($last_owner == "Warehouse") { echo "<tr> <td>$row[0]</td> <td>$row[1]</td> <td>$row[2]</td> <td>$last_date</td> <td>$last_owner</td> <td>$prev_owner</td> </tr>"; } } } // 最后闭合表格 echo "</tbody></table>";
⚠️ 小提醒:最好用参数化查询避免SQL注入,比如用pg_prepare()和pg_execute(),别直接把$serial_number拼进SQL里。
方案二:改进你的PHP循环处理方法
如果不想改SQL,也可以在PHP里先把该设备的所有历史记录存到数组里,再遍历数组取上一条记录,这样比你原来的计数器方法更可靠(原来的方法里循环完$result2后,结果集指针已经到末尾,二次fetch可能拿不到数据):
$result = pg_query($conn, "SELECT name,model,inventory_code,s_n,device_type_code FROM public.main WHERE device_type_code=1 ORDER BY main.s_n"); if (!$result) { echo "An error occurred.\n"; exit; } // 同样把表格框架放外面 echo "<table class='styled-table'> <thead> <tr> <td>Equipment name</td> <td>Model</td> <td>ID</td> <td>Last date</td> <td>Current owner</td> <td>Previous owner</td> </tr> </thead> <tbody>"; while ($row = pg_fetch_row($result)) { $serial_number = $row[3]; // 查该设备的所有历史记录,按日期正序排序(最早的在前) $result2 = pg_query($conn, " SELECT employers.employer, history.date FROM main INNER JOIN history ON main.device_code = history.device_code INNER JOIN employers ON employers.employer_code = history.employer_code WHERE main.s_n = '$serial_number' ORDER BY history.date ASC; "); // 先把所有历史记录存到数组里,方便后续取值 $history_records = []; while ($row2 = pg_fetch_assoc($result2)) { $history_records[] = $row2; } $total = count($history_records); if ($total == 0) continue; // 没有历史记录就跳过 // 最新的记录是数组最后一个元素 $last_record = end($history_records); $last_owner = $last_record['employer']; $last_date = $last_record['date']; if ($last_owner == "Warehouse") { // 上一任所有者:只有1条记录就显示无,否则取倒数第二个元素 $prev_owner = $total == 1 ? "There were no transfers" : $history_records[$total - 2]['employer']; echo "<tr> <td>$row[0]</td> <td>$row[1]</td> <td>$row[2]</td> <td>$last_date</td> <td>$last_owner</td> <td>$prev_owner</td> </tr>"; } } echo "</tbody></table>";
最后给你提个小bug修正
你原来的代码里每次循环都输出整个<table>标签,这样页面会生成N个独立的小表格,样式和体验都不好,一定要把<table>、<thead>放在循环外面,循环里只输出<tr>行,最后再闭合表格,这样才是一个完整的表格。
内容来源于stack exchange
相关产品推荐
相关产品推荐

