提问人:paulo77 提问时间:8/31/2022 最后编辑:paulo77 更新时间:9/9/2022 访问量:260
在嵌套的 while 循环中输出透视/链接表的内容 - MySQL / PHP PDO
Output The Contents Of A Pivot / Linking Table In A Nested While Loop - MySQL / PHP PDO
问:
我遇到过一种情况,我试图从与PHP的多对多关系中输出MySQL数据透视/链接表的内容。
在下面的代码中,我有一系列将包含图像的板。实际的电路板预览是用PHP的第一个块输出的,但是在里面,我需要一个嵌套的while循环来输出图像本身。
数据透视/链接表被调用有两列,它们都是表和表的外键。下面的主要代码下面给出了表格的表示形式。boards_images
board_id
image_id
boards
images
因为有些板上已经有其他板上的图像,我显然需要某种类型的条件逻辑,当相关板存在时输出图像。
每个板预览只会显示四个图像,因此我需要向MySQL添加一个子句LIMIT 4
我的问题
解决这个问题的最佳方法是什么,我做:
a) 需要执行两个数据库调用,一个在父 while 循环中,一个在嵌套的 while 循环中,或者我是否需要从具有多个 JOIN 的父 while 循环中的 MySQL 数据库中获取所有信息?
b) 如何实际输出数据透视/链接表的内容?我似乎不知道该怎么做。
<?php
// $db_id is a variable created from a user login $_SESSION value
$sql = "SELECT boards.board_id, boards.board_name, users.user_id
FROM boards
JOIN users ON boards.user_id = users.user_id
WHERE users.user_id = :user_id
ORDER BY boards.board_id DESC";
$stmt = $connection->prepare($sql);
$stmt -> execute([
':user_id' => $db_id
]);
// parent while loop that outputs the board name
while ($row = $stmt->fetch()) {
$dbBoardname = htmlspecialchars($row['board_name']);
?>
<div class="board-component">
<h2><?= $dbBoardname; ?></a></h2>
<?php
// --- NESTED INNER WHILE LOOP
$SQL2 = "SELECT boards_images.board_id, boards_images.image_id, images.filename
FROM boards_images
JOIN images ON boards_images.image_id = images.image_id
WHERE boards_images.board_id = :board_id
LIMIT 4";
$stmt2 = $connection->prepare($SQL2);
$stmt2 -> execute([
':board_id' => $dbBoardId
]);
while ($row2 = $stmt2->fetch()) {
$dbImageId = htmlspecialchars($row['image_id']);
$dbImageFilename = htmlspecialchars($row['filename']);
?>
<img src='<?= $wwwRoot . "/images-lib/{$dbImageFilename}" ?>' >
<?php } ?> <!-- end of nested while loop -->
</div>
<?php } ?> <!-- end of parent while loop -->
表格的表示形式
// 'fk' stands for foreign key
// BOARDS_IMAGES LINKING / PIVOT TABLE
+----------------+----------------+
| board_id (fk) | image_id (fk) |
+----------------+----------------+
| 1 | 23 |
| 1 | 106 |
| 1 | 55 |
| 1 | 22 |
+----------------+----------------+
// BOARDS TABLE
+-------------+--------------+---------------+
| board_id | board_name | user_id (fk) |
----------------------------------------------
| 1 | London | 21 |
| 2 | France | 21 |
+-------------+--------------+---------------+
// IMAGES TABLE
+-------------+--------------------+---------------+
| image_id | filename | user_id (fk) |
---------------------------------------------------+
| 23 | BigBen.jpeg | 21 |
| 106 | TowerBridge.jpeg | 21 |
| 55 | TheMall.jpg | 21 |
| 22 | BuckPalace.jpg | 21 |
+-------------+--------------------+---------------+
// USERS TABLE
+-----------------+----------------+
| user_id | username |
+-----------------+----------------+
| 21 | johndoe |
+-----------------+----------------+
答:
a) 需要做两个数据库调用,一个在父 while 循环中,一个在 while 循环中 在嵌套的 while 循环中,或者我是否需要从 MySQL数据库在父while循环中使用多个JOIN?
你可以做一个查询来获取所有必要的数据,我们可以限制 php 中显示的图像数量,并且不需要加入 users 表,因为你在 boards 表中有 FK:
SELECT boards.board_name, images.filename
FROM boards
INNER JOIN boards_images on boards_images.board_id = boards.board_id
INNER JOIN images on boards_images.image_id = images.image_id
WHERE boards.user_id = :user_id
b) 如何实际输出数据透视/链接表的内容?我 似乎不知道该怎么做。
上述查询的输出结果如下:
board_name | filename
-------------------------------
London | BigBen.jpeg
London | TowerBridge.jpeg
London | TheMall.jpg
London | BuckPalace.jpg
France | BigBen.jpeg
France | TowerBridge.jpeg
France | TheMall.jpg
France | BuckPalace.jpg
你的 php 循环可能看起来像这样:
<?php
// $db_id is a variable created from a user login $_SESSION value
$sql = "SELECT boards.board_name, images.filename
FROM boards
INNER JOIN boards_images on boards_images.board_id = boards.board_id
INNER JOIN images on boards_images.image_id = images.image_id
WHERE boards.user_id = :user_id";
$stmt = $connection->prepare($sql);
$stmt -> execute([
':user_id' => $db_id
]);
$dbBoardname_last = '';
$imgcount = 0;
while ($row = $stmt->fetch()) {
$dbBoardname = htmlspecialchars($row['board_name']);
$dbImageFile = "$wwwRoot/images-lib/" . htmlspecialchars($row['filename']);
//if the boardname is the same as the previous row only add images.
if($dbBoardname != $dbBoardname_last)
{
//reset the image count for new boards
$imgcount = 0;
echo "<div class=\"board-component\"><h2>$dbBoardname</a></h2>";
}
//By counting the images within the loop we can avoid using multiple or nested queries to the DB
if($imgcount < 4) {
echo "<img src=\"$dbImageFile\">";
}
$imgcount++;
if($dbBoardname != $dbBoardname_last)
{
echo '</div>';
}
//record the last board_name to check if a new board element should be created
$dbBoardname_last = $dbBoardname;
}
?>
注意:希望这段代码能按您的预期工作,但从长远来看,我认为最佳实践是将 SQL 输出解析为 JSON 对象并对其进行迭代,否则代码可能会更清晰
评论
filename
boards
filename
运行查询、循环结果以及针对每个结果运行另一个查询的方法是一种次优方法,但它有效。您可能想尝试一下,因为它很简单,而且您担心的性能问题很可能永远不会实现,请阅读以下内容:https://stackify.com/premature-optimization-evil/
当然,缺点是,您不是运行单个命令,而是运行 n + 1 命令(假设板数为 n)。查询成本很高,因此,如果您已经遇到一些与此相关的性能问题,或者想要编写性能非常高的代码,那么您可以:
- 编写单个查询
- 该子句的第一个表是
FORM
boards
LEFT JOIN
boards_images
和 ,并具有适当的标准images
- 生成的记录将包含 和 数据,如果没有足够的匹配项,则默认为
boards
images
null
SELECT ...
FROM boards
JOIN users
ON boards.user_id = users.user_id
LEFT JOIN boards_images bi1
ON boards.board_id = bi1.board_id
LEFT JOIN images i1
ON bi1.image_id = i1.image_id
LEFT JOIN boards_images bi2
ON boards.board_id = bi2.board_id AND bi2.board_id NOT IN (bi1.board_id)
LEFT JOIN images i2
ON bi2.image_id = i2.image_id
LEFT JOIN boards_images bi3
ON boards.board_id = bi3.board_id AND bi3.board_id NOT IN (bi1.board_id, bi2.board_id)
LEFT JOIN images i3
ON bi3.image_id = i3.image_id
LEFT JOIN boards_images bi4
ON boards.board_id = bi4.board_id AND bi4.board_id NOT IN (bi1.board_id, bi2.board_id, bi3.board_id)
WHERE users.user_id = :user_id
循环返回的记录并使用返回的值。我省略了子句的内容供您填写,您将需要那里的板和图像数据,图像数据列最好以图像索引为后缀,因此您可以迭代索引并在循环中构建列名。SELECT
评论
board_id