从一个叶子遍历到表中的一个或多个父级

时间:2014-12-29 04:42:31

标签: php mysql

目前我有以下Model

SELECT m.city_area_id AS city_area_id
FROM menus m
JOIN oc_category c ON (m.id = c.menu_id)
JOIN oc_category oc ON (oc.parent_id = c.category_id)
JOIN oc_product_to_category ptc ON
    ptc.category_id = oc.category_id AND
    ptc.product_id = $dish_id

此模型的预期行为是找到给出菜肴的城市区域ID。

它的工作原理如下:

  1. 通过表格oc_product_to_category查找我们的产品[我们的输入]类别ID。
  2. 使用此关系中的category_id,我们前往oc_category查找此特定产品的parent_id
  3. 一旦您达到了最顶级的类别ID,就会有menu_id [menu_id0,如果它不是最高category_id,而parent_id也是0,因为我们没有更多的父母可供我们使用。

  4. 获得menu_id后,我们就可以找到此菜单的城市区域ID。

  5. 以下是category_idparent_id的示例。如您所知,menu_id0parent_id为非零值。

    +-------------+-------+-----------+-----+--------+------------+--------+---------------------+---------------------+------------------+---------+
    | category_id | image | parent_id | top | column | sort_order | status | date_added          | date_modified       | visible_official | menu_id |
    +-------------+-------+-----------+-----+--------+------------+--------+---------------------+---------------------+------------------+---------+
    |         784 | NULL  |       783 |   0 |      0 |          1 |      1 | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 |                1 |       0 |
    +-------------+-------+-----------+-----+--------+------------+--------+---------------------+---------------------+------------------+---------+
    

    我的问题:

    我当前的SQL查询适用于每个category_id,只有一个parent_id级别可以遍历。因此,对于那些具有两个父category_id s [而不是一个]的产品,我的脚本将返回一个空集。

    我的问题是 - 无论这个特定类别ID有多少父母,我如何智能地从一个叶子遍历到最近的父母?

    编辑:看起来我的SQL行的格式化都是拙劣的。我不知道如何将水平滚动条添加到我的代码块=(

1 个答案:

答案 0 :(得分:1)

如果我了解您要执行的操作,则根据某些条件从ptc开始设置一组特定的行,例如

SELECT ptc.*
  FROM oc_product_to_category ptc
 WHERE ptc.product_id = $dish_id

然后你想获得相关的oc ...

  JOIN oc_category oc 
    ON oc.category_id = ptc.category_id

如果那不是顶级oc,你想得到它的父母......

  JOIN oc_category a1
    ON a1.category_id = oc.parent_id

如果那不是顶级,你想得到它的父母......

  JOIN oc_category a2
    ON a2.category_id = a1.parent_id

依此类推,直到你获得最多的父母。

在单个查询中执行此操作的一种方法是首先确定树的最大可能深度,然后使用外部联接操作返回每个级别的父级...

SELECT ptc.*
  FROM oc_product_to_category ptc
  JOIN oc_category a0
    ON a0.category_id = ptc.category_id
  LEFT JOIN oc_category a1 ON a1.category_id = oc.parent_id
  LEFT JOIN oc_category a2 ON a2.category_id = a1.parent_id
  LEFT JOIN oc_category a3 ON a3.category_id = a2.parent_id
  LEFT JOIN oc_category a4 ON a4.category_id = a3.parent_id
 WHERE ptc.product_id = $dish_id

现在的诀窍是确定哪些表引用(a1,a2,a3,a4)返回最顶层。

一种方法是使用一个表达式,从上到下测试每个级别,以确定返回非NULL值的最高级别。例如:

       CASE
         WHEN a4.category_id IS NOT NULL THEN a4.menu_id
         WHEN a3.category_id IS NOT NULL THEN a3.menu_id
         WHEN a2.category_id IS NOT NULL THEN a2.menu_id
         WHEN a1.category_id IS NOT NULL THEN a1.menu_id
         WHEN a0.category_id IS NOT NULL THEN a0.menu_id
       END

您可以在谓词中使用该表达式的结果来加入menus

  LEFT JOIN menus m ON m.id = expr

因此,整个查询可能看起来像这样:

SELECT m.city_area_id as city_area_id
FROM oc_product_to_category ptc
JOIN oc_category oc ON oc.category_id = ptc.category_id
LEFT JOIN oc_category a0 ON a0.category_id = oc.parent_id
LEFT JOIN oc_category a1 ON a1.category_id = a0.parent_id
LEFT JOIN oc_category a2 ON a2.category_id = a1.parent_id
LEFT JOIN oc_category a3 ON a3.category_id = a2.parent_id
LEFT JOIN oc_category a4 ON a4.category_id = a3.parent_id
LEFT JOIN menus m
    ON m.id =
        CASE
            WHEN a4.parent_id = 0 THEN a4.menu_id
            WHEN a3.parent_id = 0 THEN a3.menu_id
            WHEN a2.parent_id = 0 THEN a2.menu_id
            WHEN a1.parent_id = 0 THEN a1.menu_id
            WHEN a0.parent_id = 0 THEN a0.menu_id
        END
WHERE ptc.product_id = $dish_id

此示例查询将仅查找最多四个级别的最顶级父级;如果您的层次结构具有更大的深度,那么您需要按照相同的模式将其扩展到更多级别...

  LEFT JOIN oc_category a5 ON a5.category_id = a4.parent_id
  LEFT JOIN oc_category a6 ON a6.category_id = a5.parent_id

您需要扩展返回menu_id的表达式,以检查是否从这些级别返回了一行。

(在我的实现中,我通常使用parent_id列的NULL值来指示最顶层的节点。如果您的层次结构以不同的方式实现,则使用0的值作为parent_id,您可以需要使用不同的表达式来测试最高级别,但表达式将遵循相同的模式:

       CASE
         WHEN a4.parent_id = 0 THEN a4.menu_id
         WHEN a3.parent_id = 0 THEN a3.menu_id
         WHEN a2.parent_id = 0 THEN a2.menu_id
         WHEN a1.parent_id = 0 THEN a1.menu_id
         WHEN a0.parent_id = 0 THEN a0.menu_id
       END