设计层次结构表

时间:2015-01-22 10:17:36

标签: sql-server

让我们说我必须在我的数据库中存储以下信息, enter image description here

现在我的数据库表的设计和结构都是这样的, enter image description here

在以后的某个日子里,如果我必须添加另一个子类别级别,我将如何在不更改数据库结构的情况下实现这一目标?

我听说过将列定义为表中的行数据,并使用pivot来稍后提取详细信息......这是实现此目的的正确方法吗?

有人可以开导我或指导我正确的方向吗?提前谢谢......

:)

3 个答案:

答案 0 :(得分:5)

当要生成新级别时,很难向表中添加更多列。最好的方法是使用Hierarchy table维护Parent-Child relationship

表:Items

 x----x------------------x------------x
 | ID |      Items       | CategoryId |
 |----x------------------x------------x
 | 1  |   Pepsi          |     3      |
 | 2  |   Coke           |     3      |
 | 3  |   Wine           |     4      |
 | 4  |   Beer           |     4      |     
 | 5  |   Meals          |     2      |
 | 6  |   Fried Rice     |     2      |
 | 7  |   Black Forest   |     7      |
 | 8  |   XMas Cake      |     7      |
 | 9  |   Pinapple Juice |     8      |
 | 10 |   Apple Juice    |     8      |
 x----x------------------x------------x

表:Category

enter image description here

在类别表格中,您可以将类别添加到n级别。在Items表中,您可以存储最低级别的类别。例如,以Pepsi为例 - 其categoryId为3.在类别表中,您可以使用JOIN找到其父级,并使用层次结构查询查找父级父级。

Category表格中,ParentId的类别为空(没有parentId)将为MainCategory,而ParentId的其他项目将位于SubCategory下{1}}。

编辑:

您需要如何更改表,因为根据您当前的架构,您无法将列添加到第一个表,因为Sub类别的数量可能会不断变化。即使您根据 Rhys Jones 答案创建表格,也必须使用字符串连接两个表格。加入字符串的问题在于,当需要更改Sub categoryMain category名称时,您必须在每个表格中更改您将来遇到的麻烦并且不是很好数据库设计。所以我建议你遵循以下模式。

以下是获取子项目父项的查询。

DECLARE @ITEM VARCHAR(30) = 'Black Forest'

;WITH CTE AS
(
    -- Finds the original parent for an ITEM ie, Black Forest 
    SELECT I.ID,I.ITEMS,C.CategoryId,C.Category,ParentId,0 [LEVEL]
    FROM #ITEMS I
    JOIN #Category C ON I.CategoryId=C.CategoryId   
    WHERE ITEMS = @ITEM

    UNION ALL

    -- Now it finds the parents with hierarchy level for ITEM  
    -- ie, Black Forest. This is called Recursive query, which works like loop
    SELECT I.ID,I.ITEMS,C.CategoryId,C.Category,C.ParentId,[LEVEL] + 1
    FROM CTE I
    JOIN #Category C ON C.CategoryId=I.ParentId 
)
-- Here we keep a column to show header for pivoting ie, CATEGORY0,CATEGORY1 etc
-- and keep these records in a temporary table #NEWTABLE
SELECT ID,ITEMS,CATEGORYID,CATEGORY,PARENTID,
'CATEGORY'+CAST(ROW_NUMBER() OVER(PARTITION BY ITEMS ORDER BY [LEVEL] DESC)-1 AS VARCHAR(4)) COLS,
ROW_NUMBER() OVER(PARTITION BY ITEMS ORDER BY [LEVEL] DESC)-1 [LEVEL] 
INTO #NEWTABLE
FROM CTE
ORDER BY ITEMS,[LEVEL]
OPTION(MAXRECURSION 0)

以下是上述查询的结果

enter image description here

解释

  • Black Forest位于Cake
  • Cake位于Bakery之下。
  • Bakery位于Food

像这样,您可以为任意数量的级别创建子级或父级。现在,如果您要将父级添加到FoodBeverage,例如Food Industry,只需将Food Industry添加到Category表并保留{{1} } Food Industry'sParentId的ID为Food。就这样。

现在,如果您想进行旋转,可以按照以下步骤进行操作。

<强> 1。从列获取值以将这些值显示为数据透视中的列

Beverage

<强> 2。现在使用以下PIVOT查询

DECLARE @cols NVARCHAR (MAX)

SELECT @cols = COALESCE (@cols + ',[' + COLS + ']', '[' + COLS + ']')
               FROM    (SELECT DISTINCT COLS,[LEVEL] FROM #NEWTABLE) PV 
               ORDER BY [LEVEL] 

您将在数据透视后获得以下结果

enter image description here

注意

  1. 如果您想要所有记录而不管项目如何,请删除DECLARE @query NVARCHAR(MAX) SET @query = 'SELECT * FROM ( SELECT ITEMS, CATEGORY, COLS FROM #NEWTABLE ) x PIVOT ( MIN(CATEGORY) FOR COLS IN (' + @cols + ') ) p ORDER BY ITEMS;' EXEC SP_EXECUTESQL @query 内的WHERE子句。 Click here查看结果。

  2. 现在我已在数据透视表中提供了CTE列的顺序,即它显示了顶级父级.....项目的父级。如果您想首先显示项目的父级,最后是下级和顶级父级,您可以将DESC内的DESC更改为ROW_NUMBER()Click here查看结果。

答案 1 :(得分:3)

根据您的架构,主要类别&#39;之间没有任何关系。和&#39;子类别&#39;但你的样本数据表明会有一种关系,即酒精是一种饮料等。这听起来像是一个类别的层次结构,在这种情况下你可以改为使用一个自引用的类别表;

create table dbo.Category (
    CategoryID int not null constraint PK_Category primary key clustered (CategoryID),
    ParentCategoryID int not null,
    CategoryName varchar(100) not null
)

alter table dbo.Category add constraint FK_Category_Category foreign key(ParentCategoryID) references dbo.Category (CategoryID)

insert dbo.Category values (1, 1, 'Beverages')
insert dbo.Category values (2, 1, 'Soft Drink')
insert dbo.Category values (3, 1, 'Alcohol')

这样您就可以根据需要创建多个级别的类别。 ParentCategoryID = CategoryID的任何类别都是顶级类别。

希望这有帮助,

里斯

答案 2 :(得分:-1)

为了添加新的子类别,您应该将类​​别添加到表“ItemSubCategory1”,之后您可以轻松地将其添加到“饮料”表中。

例如: 如果有新的类别名称“Hot Drinks”和Beverages主要类别中的新项目“Coffee”(让CatId = 1,MainCatText ='Beverages'在ItemMainCategory表中)那么

INSERT INTO ItemSubCategory1(CatId,SubCatText) VALUES(4,'Hot Drinks')

INSERT INTO Drinks(ItemId,ItemName,ItemMainCategory,ItemSubCategory)
VALUES(5,'Coffee',1,4)