在MySQL中显示和分组两个连接的表

时间:2011-10-30 09:49:50

标签: php mysql

我在MySQL数据库中分组和显示两个表的结果时遇到了问题。

我正在尝试制作购物车,我已经实现了一个包含产品类别的商店菜单,并在点击类别时通过jquery,菜单展开并显示该类别的产品。

类别表示例:

+------------+
| ID | NAME  |
+------------+
| 1  | name1 |
| 2  | name2 |
| 3  | name3 |
| 4  | name4 |
+------------+

产品表示例:

+------------------------------------------------------------------------+
| ID | PR_CODE  | NAME  | DIMENSIONS | COLORS | OFFER | PRICE | CATEGORY |
+------------------------------------------------------------------------+
| 1  | pr_code1 | prod1 | 40 x 40    | blue   |   1   | 11.00 |    1     |
| 2  | pr_code2 | prod2 | 120 x 120  | white  |   1   | 12.00 |    1     |
| 3  | pr_code3 | prod3 | 60 x 120   | yellow |   0   | 13.00 |    2     |
| 4  | pr_code4 | prod4 | 40 x 60    | orange |   0   | 14.00 |    3     |
+------------------------------------------------------------------------+

产品表中的类别行,告诉哪个类别的产品属于。

菜单代码:

<div class="shopMenu">
    <ul>
    <?php
    $sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category ORDER BY categories.id DESC";
    $query = execute_select($sql);      
        foreach($query as $row) {
            $id = $row['id'];
            $name = $row['name'];
            $prodId = $row['prodId'];
            $prodName = $row['prodName'];
            echo '<li><a href="#">' . $name . '</a>
                    <ul>
                        <li><a href="&id=' . $prodId . '">' . $prodName . '</a></li>
                    </ul>               
                   </li>'; }
    ?>                
    </ul>
</div>

此代码有效,但工作错误。如果我把sql查询作为:

$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category ORDER BY categories.id DESC";

结果是:列出了所有类别,但在主菜单中重复包含更多产品的类别的次数与此类别中的产品一样多。

如果我把sql查询放在:

$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category GROUP BY categories.id ORDER BY categories.id DESC";

结果是:所有类别都列出一次,但只列出了所选类别中的一种产品,缺少其他产品。

有人可以帮助解决此问题吗?

4 个答案:

答案 0 :(得分:1)

基本上有两种方法可以做你想要的,其中两种方法都在其他回复和评论中解释过。我将在这个答案中尝试扩展它们。

一个查询

您在一个查询中检索所有数据,然后使用PHP进行一些转换,以便以适当的方式显示它们。

有两种方法可以做到这一点。您可以先修改数据然后循环它们以显示菜单,或者您可以在一个循环中完成所有操作。

两步变体

首先,我们以更“可用”的方式创建一个包含数据的数组,然后我们显示这个新数组的内容:

$sql = "SELECT categories.id as catid, categories.name as catname, products.id as prodId, products.name as prodName 
        FROM `categories` 
        INNER JOIN `products` ON categories.id = products.category 
        ORDER BY categories.id DESC";

$result = execute_select($sql);
$categories = array();
foreach($query as $row) {
    if( ! isset($categories[$row['catid']])) {
        $categories[$row['catid']] = array(
            'id' => $row['catid'],
            'name' => $row['catname'],
            'products' => array(),
        );
    }
    $categories[$row['catid']]['products'][] = array(
            'id' => $row['prodId'],
            'name' => $row['prodName'],
    );
}
foreach($categories as $cat) {
    echo '<li><a href="#">' . $cat['name'] . '</a><ul>';
    foreach($cat['products'] as $prod) {
        echo '<li><a href="&id=' . $prod['id'] . '">' . $prod['name'] . '</a></li>';
    }
    echo '</ul></li>';
}

一步变体

我们存储当前类别,当类别更改时,我们关闭当前列表并打开一个新列表:

$sql = "SELECT categories.id as catid, categories.name as catname, products.id as prodId, products.name as prodName 
        FROM `categories` 
        INNER JOIN `products` ON categories.id = products.category 
        ORDER BY categories.id DESC";

$result = execute_select($sql);
$actualcategory = null;
foreach($query as $row) {
    if($actualcategory != $row['catid']) {
        echo '<li><a href="#">' . $row['catname'] . '</a><ul>';
    }
    echo '<li><a href="&id=' . $row['prodId'] . '">' . $row['prodName'] . '</a></li>';
    if($actualcategory != $row['catid']) {
        echo '</ul></li>';
        $actualcategory = $row['catid'];
    }
}

n + 1查询

在此解决方案中,我们检索类别列表,然后为每个类别检索产品列表:

$sql = "SELECT categories.id, categories.name
        FROM `categories` 
        ORDER BY categories.id DESC";
$categories = execute_select($sql);
foreach($categories as $cat) {
    echo '<li><a href="#">' . $cat['name'] . '</a><ul>';
    $sql2 = "SELECT products.id, products.name
            FROM `products` 
            WHERE `products`.category = ".$cat['id'];
    $products = execute_select($sql2);
    foreach($products as $prod) {
        echo '<li><a href="&id=' . $prod['id'] . '">' . $prod['name'] . '</a></li>';
    }
    echo '</ul></li>';
}

干编码警告

我对前面的PHP代码进行了干编码,我甚至不确定我是不是犯了一些愚蠢的错误。您可能需要根据自己的需要进行调整。如果出现问题,请在评论中指出,我会尽快解决:)

结论

前两种可能性只执行一个查询,然后解析结果以显示有意义的信息。在我看来,代码很难理解并且容易出错。

最后一种可能性更清晰,我认为更容易修改和扩展。

从性能的角度来看,我们在前两个版本中只有一个查询,但我们检索了更多的数据和必要的连接。我认为哪种解决方案最好是没有简单的答案,它将在很大程度上取决于每种解决方案的类别和产品数量。我认为最好的办法是测试各种数据集上的每个解决方案,以确定更快的解决方案。

如果我必须开发这种菜单,我将亲自使用n + 1查询方法。即使性能略有下降(我不确定),解决方案也更加清晰,以弥补弱点。

声明

对于这个问题上的所有其他海报感到荣幸,我没有提供“新”答案,我只是将已经提供的答案放在PHP代码中。希望这会有所帮助!

答案 1 :(得分:0)

您需要两个查询。

首先获取类别列表,然后循环遍历它们。然后在每个类别中进行第二次查询并获取产品。

(是的,技术上可以在一个查询中获取所有数据,并检查该类别是否已经输出,但我建议不要这样做。)

答案 2 :(得分:0)

SELECT 
    *, products.id as prodId, products.name as prodName
FROM 
    products 
LEFT JOIN
    categories ON categories.id = products.category

您想要products表,因此请FROM products而不是FROM categories

答案 3 :(得分:0)

SQL分组运算符的大致工作方式如下:按属性分组,然后您(在引擎盖下)创建在该属性中具有相同值的行组。查询结果将仅包含每个组的顶部。您可以在每个组上使用聚合函数,但这是关于它的。

您有两种选择: (a)对每个类别(如前所述)或 (b)使用您的第一个查询,按类别和循环排序,而当前行与上一行属于同一类别,然后将其添加到当前列表中。当您到达属于上一个类别的结果时,请关闭当前列表(echo </ul>;)并开始新的