sql-server - 设计层次结构表

标签 sql-server

假设我必须将以下信息存储在我的数据库中, enter image description here

现在我的数据库表将被设计和结构如下, enter image description here

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

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

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

:)

最佳答案

当要生成新级别时,向表中添加更多列将很困难。最好的方法是使用 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 找到其父级。 s 并使用层次结构查询查找 parent 的 parent 。

Category表,类别为 ParentId为 null(即没有parentId)将是 MainCategory以及其他带有 ParentId 的项目将在SubCategory下.

编辑:

您需要如何更改表,因为根据您当前的架构,您无法向第一个表添加列,因为子类别的数量可能会不断变化。即使您按照Rhys Jones答案创建一个表,您也必须使用字符串连接两个表。与字符串连接的问题是,当需要更改Sub category时或Main 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 IndustryCategory table 和保存Food Industry's ID为ParentId对于 FoodBeverage 。就这样。

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

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

DECLARE @cols NVARCHAR (MAX)

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

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

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

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

enter image description here

注意

  1. 如果您想要所有记录而不考虑某个项目,请删除 WHERE里面的条款CTEClick here查看结果。

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

关于sql-server - 设计层次结构表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28086236/

相关文章:

sql-server - 表变量和虚拟表是一回事吗?

sql - 使用 row_number() 通过分区生成计算列

sql - 在不复制 T-SQL 中的数据的情况下更新表变量

sql - 如何使用SQL Server查找星期一到星期五的最后几周?

sql - 获取 "zero"以获取无记录日期的计数

sql - 数据透视表和连接列

sql - 在 SQL Server 中插入 "Golf"失败,错误未闭合引号

sql - 重命名表列并将其传播到依赖 View

mysql - 链接服务器查询性能缓慢

sql-server - 查询基准测试