我有个产品包含有一个FK为一个类别,该类别表中创建一种方式,每个类别可以有父母类别,例如:

Computers
    Processors
        Intel
            Pentium
            Core 2 Duo
        AMD
            Athlon

我需要做一个选择的查询,如果所选择的类别处理,它将返回的产品,是在英特尔处理器,芯2Duo,Amd,等等。

我以为关于创建某种"高速缓冲存储器",将储存所有类别层次结构中的每一个类别中的数据库和包括",在"在那里的条款。这是最好的解决办法吗?

有帮助吗?

解决方案

最好的解决方案是在数据库的设计阶段。你的分类表,需要一个 嵌套.该文章 管理层级数据中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 记录。

其他提示

看起来像一个公共表表达式作业..沿的线的东西:

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

这应该选择他的名字是“处理器”和任何它的后代范畴,应该能够使用IN子句中拉回来的产品。

我已经做过类似的事情过去,首先查询该类别的标识,然后查询的产品"中的"那些类别。得到该类别的硬点,你有一些选择:

  • 如果级别的嵌套的类别是已知的或者你可以找到一个上限:建立一个可怕看的选择有很多的联接。这是快速的,但是丑陋的,你需要设定一个上限水平的层次结构。
  • 如果你有一个相对较小数量的总的类别,查询它们的所有(只是身份、父母),收集身份证的那个你关心,并做。在对产品。这是合适的选择我。
  • 查询上/下层级采用一系列的选择。简单、但是比较缓慢。
  • 我相信,最近的版本Sql server有一些支持递归的查询,但还没有使用他们自己。

存储程序可以帮助如果你不想做这个程序。

您想找到什么是类“父母”关系的传递闭包。我想,没有限制的类别层次深度,所以你不能制定一个SQL查询,该查询查找所有类别。我会做(在伪代码)是这样的:

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

所以,还是想继续,直到不再发现查询孩子。这表现良好在速度方面,除非你有退化的层次结构(比如,1000类别,每个类别的另一个孩子),或大量总类别。在第二种情况下,你总是可以使用临时表工作,以保持您的应用程序和数据库小之间的数据传输。

也许是这样的:

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)

[编辑]如果类别深度大于一个这种更大的将形成最内层的查询。我怀疑,你可以设计一个存储过程,将在表中向下钻取,直到由内部查询返回的ID没有孩子 - 可能是更好的有痕类作为层次结构的末端节点的属性 - 然后执行对这些ID外部查询。

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 *从r

此应该递归向下从给定类别启动所有“子”类别。

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

我喜欢使用一个堆栈临时表为分层数据。 这里有一个粗糙的例子 -

-- 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

这里有一个很好的解释链接文本

我的回答从几天前另一个问题也适用于这里...... 递归在SQL

有在我联系的那本书应该很好地护住情况的一些方法。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top