1

3 つのテーブルがあるとします (重要な列のみ)。

  1. カテゴリ (catId key、parentCatId)
  2. Category_Hierarchy (catId key、parentTrail、catLevel)
  3. 製品 (prodId key、catId、createdOn)

auto_increment別の Category_Hierarchy テーブルを持つ理由があります。これは、MySql トリガーが同じように機能し、値を使用したい場合にトリガー内の同じテーブルに列を設定できないためです。 . この問題のために、これは無関係です。とにかく、これらの 2 つのテーブルは 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 未満) の完全な下位ツリーを返します。ルート ノードを除外することは、もう 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 への最も頻繁な呼び出しの 1 つになるためです。

編集

写真は 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

編集

最新の明確化により、このソリューションは単純に編集されて、カテゴリ カーソル クエリが簡素化されました。

: 5 行目の VARCHAR を、parentTrail 列に基づいて適切なサイズにします。

于 2010-02-03T19:15:07.920 に答える