1

假设我有 3 个表(仅重要列)

  1. 类别(catId key,parentCatId)
  2. Category_Hierarchy (catId key, parentTrail, catLevel)
  3. 产品(prodId key、catId、createdOn)

有一个单独的 Category_Hierarchy 表是有原因的,因为我在 Category 表上使用了填充它的触发器,因为 MySql 触发器像它们一样工作,如果我想使用auto_increment值,我不能在触发器内的同一个表上填充列. 对于这个问题,这是无关紧要的。无论如何,这两个表是 1:1。

类别表可以是:

+-------+-------------+
| catId | parentCatId |
+-------+-------------+
|   1   | NULL        |
|   2   | 1           |
|   3   | 2           |
|   4   | 3           |
|   5   | 3           |
|   6   | 4           |
|  ...  | ...         |
+-------+-------------+

类别_层次结构

+-------+-------------+----------+
| catId | parentTrail | catLevel |
+-------+-------------+----------+
|   1   | 1/          | 0        |
|   2   | 1/2/        | 1        |
|   3   | 1/2/3/      | 2        |
|   4   | 1/2/3/4/    | 3        |
|   5   | 1/2/3/5/    | 3        |
|   6   | 1/2/3/4/6/  | 4        |
|  ...  | ...         | ...      |
+-------+-------------+----------+

产品

+--------+-------+---------------------+
| prodId | catId | createdOn           |
+--------+-------+---------------------+
| 1      | 4     | 2010-02-03 12:09:24 |
| 2      | 4     | 2010-02-03 12:09:29 |
| 3      | 3     | 2010-02-03 12:09:36 |
| 4      | 1     | 2010-02-03 12:09:39 |
| 5      | 3     | 2010-02-03 12:09:50 |
| ...    | ...   | ...                 |
+--------+-------+---------------------+

Category_Hierarchy 使获取类别从属树变得简单,如下所示:

select c.*
from Category c
    join Category_Hierarchy h
    on (h.catId = c.catId)
where h.parentTrail like '1/2/3/%'

它将返回类别 3 的完整从属树(即低于 2,即低于 1,即根类别),包括从属树根节点。排除根节点只是又一个where条件。

问题

我想写一个存储过程:

create procedure GetLatestProductsFromSubCategories(in catId int)
begin
    /* return 10 latest products from each */
    /* catId subcategory subordinate tree  */
end;

这意味着如果某个类别有 3 个直接子类别(下面有任意数量的节点),我将得到 30 个结果(每个从属树中 10 个)。如果它有 5 个子类别,我会得到 50 个结果。

最好/最快/最有效的方法是什么?如果可能的话,我想避免使用游标,除非它们与任何其他解决方案以及准备好的语句相比工作得更快,因为这将是对 DB 的最频繁调用之一。

编辑

由于一张图片可以讲述 1000 个单词,因此我将尝试使用图片更好地解释我想要什么。下图显示了类别树。这些节点中的每一个都可以有任意数量的与之相关的产品。图片中不包含产品。

类别树

所以如果我执行这个调用:

call GetLatestProductsFromSubCategories(1);

我想有效地获得 30 种产品:

  • 来自整个橙色子树的 10 款最新产品
  • 来自整个蓝色子树的 10 款最新产品和
  • 来自整个绿色子树的 10 款最新产品

我不想从节点下的每个节点获得 10 个最新产品,catId=1这意味着 320 个产品。

4

1 回答 1

2

最终解决方案

该解决方案具有 O(n) 性能:

CREATE PROCEDURE foo(IN in_catId INT)
BEGIN
  DECLARE done BOOLEAN DEFAULT FALSE;
  DECLARE first_iteration BOOLEAN DEFAULT TRUE;
  DECLARE current VARCHAR(255);

  DECLARE categories CURSOR FOR
  SELECT parentTrail 
  FROM category 
  JOIN category_hierarchy USING (catId)
  WHERE parentCatId = in_catId;
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE;

  SET @query := '';

  OPEN categories;

  category_loop: LOOP
    FETCH categories INTO current;
    IF `done` THEN LEAVE category_loop; END IF;

    IF first_iteration = TRUE THEN
      SET first_iteration = FALSE;
    ELSE
      SET @query = CONCAT(@query, " UNION ALL ");
    END IF;

    SET @query = CONCAT(@query, "(SELECT product.* FROM product JOIN category_hierarchy USING (catId) WHERE parentTrail LIKE CONCAT('",current,"','%') ORDER BY createdOn DESC LIMIT 10)");

  END LOOP category_loop;
  CLOSE categories;

  IF @query <> '' THEN
    PREPARE stmt FROM @query;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  END IF;

END

编辑

由于最新的澄清,这个解决方案被简单地编辑以简化类别游标查询。

注意:根据您的 parentTrail 列,将第 5 行的 VARCHAR 设置为适当的大小。

于 2010-02-03T19:15:07.920 回答