从分层数据中获取有限数量的记录

时间:2010-02-03 17:55:28

标签: mysql stored-procedures hierarchical-data

假设我有3个表(仅限有效列)

  1. 类别(catId key,parentCatId)
  2. Category_Hierarchy(catId key,parentTrail,catLevel)
  3. 产品(prodId key,catId,createdOn)
  4. 有一个单独的Category_Hierarchy表是有原因的,因为我正在使用填充它的Category表上的触发器,因为MySql触发器正如他们那样工作,如果我愿意,我无法在触发器内的同一个表上填充列使用auto_increment值。为了这个问题,这是无关紧要的。无论如何,这两个表格是1:1。

    类别表可以是:

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

    Category_Hierarchy

    +-------+-------------+----------+
    | 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个单词,我会尝试用图像更好地解释我想要的东西。下图显示了类别树。 每个节点都可以拥有与之相关的任意数量的产品。产品不包括在图片中。

    category tree http://i50.tinypic.com/1sfnmv.jpg

    所以,如果我执行此调用:

    call GetLatestProductsFromSubCategories(1);
    

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

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

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

1 个答案:

答案 0 :(得分: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成为合适的大小。