【问题标题】:SQL Break down path column into hierarchical dataSQL 将路径列分解为分层数据
【发布时间】:2017-08-07 22:27:00
【问题描述】:

现有表如下所示:

Categories
----------------------------------------------
Drinks\Soda
Drinks\Juice\Fruit Juice\Apple Juice\Sugar Free
Food\Fruit\Oranges
Food\Fruit\Apples\Golden
Food\Fruit\Apples\Red Delicious
Food\Vegetables\Potatoes
Food\Meat
Food

而且我需要将其处理成分层数据表,如下所示:

Id  | ParentId |  Category   | Full path
------------------------------------------
1   | null     | Drinks      | Drinks
2   | 1        | Soda        | Drinks\Soda
3   | 1        | Juice       | Drinks\Juice
4   | 3        | Fruit Juice | Drinks\Juice\Fruit Juice
5   | 4        | Apple Juice | Drinks\Juice\Fruit Juice\Apple Juice
6   | 5        | Sugar Free  | Drinks\Juice\Fruit Juice\Apple Juice\Sugar Free
7   | null     | Food        | Food
8   | 7        | Fruit       | Food\Fruit
9   | 8        | Oranges     | Food\Fruit\Oranges
10  | 8        | Apple       | Food\Fruit\Apples

我使用的是 SQL Server 2012。

我以为我有交叉应用,但后来我得到了每个父级的多行,我最终得到了类似于下表的东西,这不是我需要的:

Category   | Full path
------------------------------------------
Drinks      | Drinks\Soda
Soda        | Drinks\Soda
Drinks      | Drinks\Juice
Juice       | Drinks\Juice

编辑:这是我目前所拥有的:

CREATE TABLE [dbo].[food_categories3](
    [id] [int] NOT NULL,
    [category] [varchar](350) NULL)

insert into food_categories3
values 
(1,'Drinks\Soda'),
(2,'Drinks\Juice\Fruit Juice\Apple Juice\Sugar Free'),
(3,'Food\Fruit\Oranges'),
(4,'Food\Fruit\Apples\Golden'),
(5,'Food\Fruit\Apples\Red Delicious'),
(6,'Food\Vegetables\Potatoes'),
(7, 'Food\Meat'),
(8,'Food')

select * from food_categories3

 SELECT distinct X.category,
     splitted.x.value('.', 'VARCHAR(100)') AS cat  
 FROM  (SELECT  category,  
         CAST ('<M>' + REPLACE(category, '\', '</M><M>') + '</M>' AS XML) AS cat  
     FROM  food_categories3) AS X CROSS APPLY cat.nodes ('/M') AS splitted(x) 
order by category

另外,我的实际数据集不是关于水果类别的,这只是我的数据结构的一个简单示例。


编辑 2:我现在的主要问题是,如何在插入子代时跟踪父行,例如我如何判断 Oranges 和 Apples 都是“Fruit”的子代。

【问题讨论】:

  • 你能分享一下你到目前为止所做的努力的代码吗?
  • 首先你需要一个分割函数——网上有很多可用的,找一个。
  • 是的,我已经拆分了数据,并用一个可运行的示例更新了我的问题。

标签: sql sql-server


【解决方案1】:

这里有一个想法,使用递归 CTE 对父子关系进行排序。

我已经开始使用您的查询作为源而不更改它(CTE_Source)。接下来的两个 CTE 是一些数据准备。

第一个 CTE - 剥离找到的类别的所有权利以获得正确的路径并过滤不同的值。

第二个 CTE - 现在使用之前创建的路径,我们可以分配 ID(使用 DENSE_RANK 函数)并计算每一行的级别(斜线计数)

现在终于使用计算出的 Path 和 Level 列,递归查找每个类别的 ParentID 应该不会太难

WITH CTE_Source AS 
(
  SELECT distinct X.category,
     splitted.x.value('.', 'VARCHAR(100)') AS cat  
 FROM  (SELECT  category,  
         CAST ('<M>' + REPLACE(category, '\', '</M><M>') + '</M>' AS XML) AS cat  
     FROM  food_categories3) AS X CROSS APPLY cat.nodes ('/M') AS splitted(x) 
)
, CTE_Prep1 AS
(
    SELECT DISTINCT
     cat
    , LEFT(category, PATINDEX ('%'+cat+'%', category) + LEN(cat) - 1) AS Path
    FROM CTE_Source s
)
, CTE_Prep2 AS 
(
    SELECT 
    DENSE_RANK() OVER (ORDER BY Path) AS ID
    , * 
    , LEN(Path) - LEN(REPLACE(Path, '\', '')) AS Level
    FROM CTE_Prep1
)
, RCTE AS 
(
    SELECT *, CAST(NULL AS BIGINT) AS ParentID
    FROM CTE_Prep2
    WHERE Level = 0

    UNION ALL 

    SELECT p.*, r.ID
    FROM CTE_Prep2 p
    INNER JOIN RCTE r ON p.Level = r.Level +1 AND p.Path LIKE r.Path + '%'
)
SELECT * FROM RCTE
ORDER BY ID

SQLFiddle DEMO

【讨论】:

    猜你喜欢
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多