使用单个查询获取类别和文章


Get categories and articles with a single query

我得到了两个SQL表:

  1. 类别:id、标题、级别、父级
  2. 文章:id,title,category_id

现在我想得到这个结果:

Category 1
    Article 1
    Artcile 2
    Article 3
Category 2
    Subcategory 1
        Article 1
        Artcile 2
        Article 3
    Subcategory 2
        Article 1
        Artcile 2
        Article 3
Category 3
    Article 1
    Artcile 2
    Article 3

我会这样做:

$categories = $db->prepare('SELECT * FROM categories WHERE level = 0');
$categories->execute();
while($category = $categories->fetch(PDO::FETCH_OBJ)) {
    $sub_categories = $db->prepare('SELECT * FROM categories WHERE parent = :parent_id');
    $sub_categories->bindParam(':parent_id', $category->id, PDO::PARAM_INT);
    $sub_categories->execute();
    while($sub_category = $sub_categories->fetch(PDO::FETCH_OBJ)) {
        $articles = $db->prepare('SELECT * FROM articles WHERE category_id = :category_id');
        $articles->bindParam(':category_id', $sub_category->id, PDO::PARAM_INT);
        $articles->execute();
        while($article = $articles->fetch(PDO::FETCH_OBJ)) {
            echo $article->title.'<br />';
        }
    }
}

首先,这很糟糕,因为它只从子类别中获取文章(而不是针对类别),而且只有一个级别的深度选择。

第二:我使用了三个查询,它们被执行多次,只是为了做一些基本的事情。性能…:-(

我只想用一个SQL查询来解决这个问题。这可能吗?(然后我会创建输出…)

SQLfiddle:http://sqlfiddle.com/#!2/2a4f2

您可以尝试以下语句:

SELECT
    A.id, A.title,
    C.id AS category_id, C.title AS category_title, C.level AS category_level,
    P.id AS parent_id, P.title AS parent_title, P.level AS parent_level
FROM articles AS A
    INNER JOIN categories AS C ON A.category_id=C.id
    LEFT JOIN categories AS P ON C.parent=P.id

这将为您提供所有文章,包括主要类别和(可选)父类别。但你必须考虑,这个类别可以是主要类别,也可以是次要类别。

你可以用这种方式改变它,总是直接获得主分类和子分类,并对其进行排序

SELECT
    IFNULL(P.id, C.id) AS category_id, IFNULL(P.title, C.title) AS category_title,
    IF(P.id IS NULL, NULL, C.id) AS subcategory_id, IF(P.title IS NULL, NULL, C.title) AS subcategory_title,
    A.id, A.title
FROM articles AS A
    INNER JOIN categories AS C ON A.category_id=C.id
    LEFT JOIN categories AS P ON C.parent=P.id
ORDER BY category_id, subcategory_id, A.id

IFNULL查找是否存在父类别。如果是,则父P为主类别;如果不是,则类别C为主。下一行检查父P是否为NULL。如果是,则不存在子类别;如果否,则类别C是子类别。看起来有点不愉快,但对于两个级别来说,它运行良好:

http://sqlfiddle.com/#!2/0ed4d/4