【问题标题】:MySQL - Multiple level product databaseMySQL - 多级产品数据库
【发布时间】:2018-09-23 01:25:38
【问题描述】:

我目前正在为我的一位客户构建一个基于商店的模块。这些项目有许多不同类型的类别,它们可以输入。我将使用的一个示例是收音机。

它可以是手持移动类型的收音机。我们在以下路径下列出了收音机:

store -> radios -> brand -> mobile / handheld -> item page

如果所有内容的布局都相同,那么存储起来非常简单,但可惜并不是那么容易。他们可能有一个适用于所有收音机和品牌的配件,在这种情况下,它的存放方式如下:

store -> radios -> accessory -> item page

这是单选下方的完整类别,因此对于每个不太理想的项目组,编写 select 必须有所不同。这些项目也应该可以从共享的父类别中访问,就像我要访问 store -> radios 我应该看到这些子类别下的所有项目。这是我计划的当前数据库结构,我认为这是我遇到最多问题的地方(P.S:ID 类别是唯一的、主要的和自动递增的):

+===================================================+
| ID | Category Name  | Path            | Parent ID |
+===================================================+
| 1  | radios         | radios          | 0         |
| 2  | accessory      | misc            | 1         |
| 3  | motorola       | motorola        | 1         |
| 4  | handheld       | handheld        | 3         |
| 5  | mobile         | mobile          | 3         |
+===================================================+

然后我将产品上的category_id 设置为说.. 5 用于我添加到产品页面的移动收音机。如果我访问产品页面,我需要获取每个父类别的路径以显示在页面上,例如

store/radios/motorola/mobile/product-page-here.html

我必须使用路径字段从上述数据库构建该路径并加入所有父类以获得正确的路径。现在,我正在做很多 select * FROM tblname WHERE id = parent 然后在该命令的 while 循环中,以相同的方式进行另一个选择,直到我回到父表。必须有更好的方法来做到这一点,特别是因为如果我只深入 3 层,而客户添加了 4 层的子类别,它永远不会走在正确的道路上。

最后一个问题是,使用上面的示例,如果我改为转到store/radios/motorola,它将尝试获取category_id 下列出的任何项目3 而不是5,并且该项目不会显示在motorola 的常规选项卡下,因为它是mobile 表的子代,motorola 表的间接子代。通过为他们提供超过 1 个子类别选项,这使得规划整个系统变得非常痛苦。

无论如何,感谢您的阅读,请在下方留下您的 cmets 或建议。顺便说一句,这一切都存储在 MySQL 中并从 PHP 脚本中调用。

【问题讨论】:

    标签: php mysql database mysqli


    【解决方案1】:

    您是severely limited by your current choice of schema.(实际上,我很想将您的问题作为我在此处链接的副本来结束,但是鉴于您的问题描述,我建议对具体化路径进行变体:

    不要从描述性属性的层次结构开始,而是将它们视为与每个项目相关联的词云,并考虑由一个或多个词组成的每条可能路径。例如

     radios/sku1
     motorola/sku1
     mobile/sku1
     radios/Motorola/sku1
     radios/mobile/sku1
     motorola/radios/sku1
     motorola/mobile/sku1
     mobile/radios/sku1
     mobile/Motorola/sku1
     radios/Motorola/mobile/sku1
     radios/mobile/motorola/sku1
     (etc)
    

    您是否存储这条路径取决于您 - 如上所示,条目的数量会迅速增加。但是您可以即时生成它。例如

    Create table userpath (
       request int, 
       word varchar(30),
       Primary key (request, word)
    );
    Create table product (
       SKU int auto-increment,
       ...make, model, price, description...
    );
    Create table labels (
       SKU int,
       Word varchar(20),
       Primary key (SKU, word),
       Uniques key (word, SKU)
    );
    

    要解析路径中的项目,请将其拆分为单词,然后将单词放入 userpath 中:

    Select p.*
    From products p
    Join labels l
    On p.sku=l.sku
    Join userpath u
    On l.word=u.word
    Where request = ?
    

    并且可以从以下位置获取当前路径的子类别:

    Select l2.word, count(*)
    From labels L2
    join products p
    on l2.sku=p.sku
    Join labels l
    On p.sku=l.sku
    Join userpath u
    On l.word=u.word
    Left join userpath U2
    On l2.word=u2.word
    Where u2.word is null
    And request=?
    Order by count(*) desc
    

    (这可以通过权重和元数据来增强)

    【讨论】:

      【解决方案2】:

      正如@symcbean 所提到的,这种模式是相当有限的,并且将您的品牌存储为类别可能不是您想要做的。但是,根据您现有的情况,这里有一些关于如何解决您的问题的建议。

      除非您可以对嵌套类别的深度设置硬性限制,否则这些问题在应用程序代码中可能比 SQL 更容易解决。编写一个查询可以很容易地从这样的表中提取给定深度的嵌套类别。比如:

      SELECT t1.name, t2.name, t3.name FROM accessory_categories AS t1
      LEFT JOIN accessory_categories AS t2 ON t2.parent_id=t1.id
      LEFT JOIN accessory_categories AS t3 ON t3.parent_id=t2.id
      WHERE t1.parent_id!=1
      AND t1.id!=1;
      

      但正如你所说,一旦有人创建了第四层,你就有麻烦了。

      只要这将是一个具有合理数量类别的小型商店,您最好的选择可能是创建几个数据结构来将您的类别映射到它们的父级,反之亦然,然后将它们填充到缓存中像 Redis 或 Memcache 这样您就不必为每个请求提取整个类别表。

      <?php
      //Mock category records, would come from the DB in the real world
      $categoryRecords = [
          ['id' => 1, 'title' => 'Radios', 'slug'=>'radios', 'parent_id' => 0],
          ['id' => 2, 'title' => 'Accessories', 'slug'=>'misc', 'parent_id' => 1],
          ['id' => 3, 'title' => 'Motorola', 'slug'=>'motorola', 'parent_id' => 1],
          ['id' => 4, 'title' => 'Handheld', 'slug'=>'handheld', 'parent_id' => 3],
          ['id' => 5, 'title' => 'Mobile', 'slug'=>'mobile', 'parent_id' => 3],
          ['id' => 6, 'title' => 'Level 3', 'slug'=>'level-3', 'parent_id' => 5],
          ['id' => 7, 'title' => 'Level 4', 'slug'=>'level-4', 'parent_id' => 6]
      ];
      
      //Create an array that maps parent IDs to primary keys
      $idToParentIdMap = [];
      $parentIdToIdMap = [];
      
      foreach($categoryRecords as $currRecord)
      {
          $idToParentIdMap[$currRecord['id']] = $currRecord['parent_id'];
          $parentIdToIdMap[$currRecord['parent_id']][] = $currRecord['id'];
      }
      
      /*
       * Now would be a good time to cache these maps in something like Redis or Memcache so you don't have to pull
       * the whole category table during every request.
       */
      
      
      /**
       * This function will traverse the category tree and return an array of IDs belonging to all categories below
       * the category identified by the $parentId
       * @param int $parentId Primary key of category to find children for
       * @param array $childIds Pass previously found IDs for recursion
       * @return array
       */
      function findChildIds($parentId, $childIds=[])
      {
          global $parentIdToIdMap;
      
          if(array_key_exists($parentId, $parentIdToIdMap))
          {
              $immediateChildIds = $parentIdToIdMap[$parentId];
      
              foreach($immediateChildIds as $currChildId)
              {
                  $childIds[] = $currChildId;
      
                  $childIds = findChildIds($currChildId, $childIds);
              }
          }
      
          return $childIds;
      }
      
      /**
       * Return an array of parent IDs for a given category ID
       * @param int $categoryId
       * @return array
       */
      function findParentIds($categoryId)
      {
          global $idToParentIdMap;
      
          $parentIds=[];
      
          while(array_key_exists($categoryId, $idToParentIdMap))
          {
              $currParentId = $idToParentIdMap[$categoryId];
              $parentIds[] = $currParentId;
      
              $categoryId = $currParentId;
          }
      
          $parentIds = array_reverse($parentIds);
      
          return $parentIds;
      }
      
      //ID of category requested
      $requestedId = 3;
      
      /*
       * Now you can use these IDs to find all of the sub categories and products under the requested category
       */
      $categoryIds = findChildIds($requestedId);
      $categoryIds[] = $requestedId;
      
      /*
       * Now you can use these IDs to get your parent categories
       */
      $parentIds = findParentIds($requestedId);
      

      【讨论】:

      • 你的代码给了我一个想法。我不会像我一直在尝试那样尝试仅选择当前类别的数据,而是选择所有数据并构建一个完整的数组,因为类别列中没有很多数据。然后我可以遍历我的数组并使用它构建我需要的数据,因为它不会在这么小的请求下使用太多内存。在显示页面上,我将做类似的事情来建立一个孩子列表,看看产品是否与那个孩子数组进行比较以调用它们。一旦它变成一个巨大的数据库,那么我可能会遇到问题,但暂时不会
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      • 1970-01-01
      • 2014-06-26
      • 2013-09-16
      • 1970-01-01
      相关资源
      最近更新 更多