8

カテゴリの FK を含む製品テーブルがあります。カテゴリ テーブルは、各カテゴリが親カテゴリを持つことができる方法で作成されます。例:

Computers
    Processors
        Intel
            Pentium
            Core 2 Duo
        AMD
            Athlon

選択したカテゴリがプロセッサの場合、Intel、Pentium、Core 2 Duo、Amd などの製品を返すという選択クエリを作成する必要があります...

データベース内のすべてのカテゴリの階層にすべてのカテゴリを格納し、where 句に「IN」を含める、ある種の「キャッシュ」を作成することを考えました。これが最善の解決策ですか?

4

9 に答える 9

6

これに対する最善の解決策は、データベースの設計段階にあります。カテゴリ テーブルはNested Setである必要があります。記事Managing Hierarchical Data in MySQLは (タイトルにもかかわらず) MySQL 固有のものではなく、データベース テーブルに階層を格納するさまざまな方法の概要を説明しています。

エグゼクティブサマリー:

ネストされたセット

  • どんな深さでも簡単に選択できます
  • 挿入と削除は難しい

標準のparent_idベースの階層

  • 選択は内部結合に基づいています(そのため、毛むくじゃらになります)
  • 挿入と削除は簡単です

あなたの例に基づいて、階層テーブルがネストされたセットである場合、クエリは次のようになります。

SELECT * FROM products 
   INNER JOIN categories ON categories.id = products.category_id 
WHERE categories.lft > 2 and categories.rgt < 11

2 と 11 はそれぞれProcessorsレコードの左と右です。

于 2008-10-13T13:35:55.573 に答える
4

共通テーブル式の仕事のように見えます..次の行に沿った何か:

with catCTE (catid, parentid)
as
(
select cat.catid, cat.catparentid from cat where cat.name = 'Processors'
UNION ALL
select cat.catid, cat.catparentid from cat inner join catCTE on cat.catparentid=catcte.catid
)
select distinct * from catCTE

名前が「Processors」であるカテゴリとその子孫のいずれかが選択され、それを IN 句で使用して製品を引き戻すことができるはずです。

于 2008-10-13T13:35:16.120 に答える
0

数日前の別の質問に対する私の答えはここに当てはまります... SQLでの再帰

私がリンクした本には、あなたの状況をうまくカバーする方法がいくつかあります。

于 2008-10-15T22:08:24.103 に答える
0
CREATE TABLE #categories (id INT NOT NULL, parentId INT, [name] NVARCHAR(100))
INSERT INTO #categories
    SELECT 1, NULL, 'Computers'
    UNION
SELECT 2, 1, 'Processors'
    UNION
SELECT 3, 2, 'Intel'
    UNION
SELECT 4, 2, 'AMD'
    UNION
SELECT 5, 3, 'Pentium'
    UNION
SELECT 6, 3, 'Core 2 Duo'
    UNION
SELECT 7, 4, 'Athlon'
SELECT * 
    FROM #categories
DECLARE @id INT
    SET @id = 2
            ; WITH r(id, parentid, [name]) AS (
    SELECT id, parentid, [name] 
        FROM #categories c 
        WHERE id = @id
        UNION ALL
    SELECT c.id, c.parentid, c.[name] 
        FROM #categories c  JOIN r ON c.parentid=r.id
    )
SELECT * 
    FROM products 
    WHERE p.productd IN
(SELECT id 
    FROM r)
DROP TABLE #categories   

このようにそのまま実行すると、例の最後の部分は実際には機能しません。製品から選択を削除し、単純な SELECT * FROM r に置き換えるだけです

于 2008-10-13T12:56:45.620 に答える
0

私は過去に同様のことを行いました.最初にカテゴリIDを照会し、次にそれらのカテゴリの「IN」製品を照会しました. カテゴリを取得するのは難しい作業であり、いくつかのオプションがあります。

  • カテゴリの入れ子のレベルがわかっている場合、または上限を見つけることができる場合: たくさんの JOIN を使用して見栄えの悪い SELECT を作成します。これは高速ですが、見苦しく、階層のレベルに制限を設定する必要があります。
  • 総カテゴリ数が比較的少ない場合は、それらすべて (ID、親のみ) を照会し、関心のあるカテゴリの ID を収集して、製品に対して SELECT....IN を実行します。これは私にとって適切なオプションでした。
  • 一連の SELECT を使用して階層を上下にクエリします。シンプルですが、比較的遅いです。
  • SQLServer の最近のバージョンでは、再帰クエリがある程度サポートされていると思いますが、自分では使用していません。

アプリ側でこれを行いたくない場合は、ストアド プロシージャが役立ちます。

于 2008-10-13T12:20:13.137 に答える
0

見つけたいのは、カテゴリ「親」関係の推移閉包です。カテゴリ階層の深さに制限はないと思うので、すべてのカテゴリを検索する単一の SQL クエリを作成することはできません。私が(疑似コードで)行うことは次のとおりです。

categoriesSet = empty set
while new.size > 0:
  new = select * from categories where parent in categoriesSet
  categoriesSet = categoriesSet+new

そのため、子が見つからなくなるまで、子のクエリを続けます。これは、縮退した階層 (たとえば、それぞれが別の子である 1000 のカテゴリ) または多数の総カテゴリがない限り、速度の点で適切に動作します。2 番目のケースでは、常に一時テーブルを使用して、アプリとデータベース間のデータ転送を小さく保つことができます。

于 2008-10-13T12:24:08.900 に答える
0

たぶん次のようなもの:

select *
from products
where products.category_id IN
  (select c2.category_id 
   from categories c1 inner join categories c2 on c1.category_id = c2.parent_id
   where c1.category = 'Processors'
   group by c2.category_id)

[編集] カテゴリの深さが 1 より大きい場合、これは最も内側のクエリを形成します。内部クエリによって返された ID に子がなくなるまでテーブルをドリルダウンするストアド プロシージャを設計できるのではないかと思います。おそらく、カテゴリを階層内のターミナル ノードとしてマークする属性を設定する方がよいでしょう。それらの ID に対して外側のクエリを実行します。

于 2008-10-13T12:28:13.620 に答える
0

これにより、特定のカテゴリから始まるすべての「子」カテゴリが再帰されます。

DECLARE @startingCatagoryId int
DECLARE @current int
SET @startingCatagoryId = 13813 -- or whatever the CatagoryId is for 'Processors'

CREATE TABLE #CatagoriesToFindChildrenFor
(CatagoryId int)

CREATE TABLE #CatagoryTree
(CatagoryId int)

INSERT INTO #CatagoriesToFindChildrenFor VALUES (@startingCatagoryId)

WHILE (SELECT count(*) FROM #CatagoriesToFindChildrenFor) > 0
BEGIN
    SET @current = (SELECT TOP 1 * FROM #CatagoriesToFindChildrenFor)

    INSERT INTO #CatagoriesToFindChildrenFor
    SELECT ID FROM Catagory WHERE ParentCatagoryId = @current AND Deleted = 0

    INSERT INTO #CatagoryTree VALUES (@current)
    DELETE #CatagoriesToFindChildrenFor WHERE CatagoryId = @current
END

SELECT * FROM #CatagoryTree ORDER BY CatagoryId

DROP TABLE #CatagoriesToFindChildrenFor
DROP TABLE #CatagoryTree
于 2008-10-13T13:40:41.867 に答える
0

階層データにスタック一時テーブルを使用するのが好きです。ここに大まかな例があります -

-- create a categories table and fill it with 10 rows (with random parentIds)
CREATE TABLE Categories ( Id uniqueidentifier, ParentId uniqueidentifier )
GO

INSERT
INTO   Categories
SELECT NEWID(),
       NULL 
GO

INSERT
INTO   Categories
SELECT   TOP(1)NEWID(),
         Id
FROM     Categories
ORDER BY Id
GO 9


DECLARE  @lvl INT,            -- holds onto the level as we move throught the hierarchy
         @Id Uniqueidentifier -- the id of the current item in the stack

SET @lvl = 1

CREATE TABLE #stack (item UNIQUEIDENTIFIER, [lvl] INT)
-- we fill fill this table with the ids we want
CREATE TABLE #tmpCategories (Id UNIQUEIDENTIFIER)

-- for this example we’ll just select all the ids 
-- if we want all the children of a specific parent we would include it’s id in
-- this where clause
INSERT INTO #stack SELECT Id, @lvl FROM Categories WHERE ParentId IS NULL

WHILE @lvl > 0
BEGIN -- begin 1

      IF EXISTS ( SELECT * FROM #stack WHERE lvl = @lvl )
      BEGIN -- begin 2

      SELECT @Id = [item]
      FROM #stack
      WHERE lvl = @lvl

      INSERT INTO #tmpCategories
      SELECT @Id

      DELETE FROM #stack
      WHERE lvl = @lvl
      AND item = @Id

      INSERT INTO #stack
      SELECT Id, @lvl + 1
      FROM   Categories
      WHERE  ParentId = @Id

      IF @@ROWCOUNT > 0
      BEGIN -- begin 3
         SELECT @lvl = @lvl + 1
      END -- end 3
   END -- end 2
   ELSE
   SELECT @lvl = @lvl - 1

END -- end 1

DROP TABLE #stack

SELECT * FROM #tmpCategories
DROP TABLE #tmpCategories
DROP TABLE Categories

ここに良い説明がありますリンクテキスト

于 2008-10-15T21:56:23.707 に答える