将编号类别转换为命名类别

Convert numbered categories to named categories

我相信一定有一个简单的解决方案。我有两个 tables,一个是库存数据,另一个是保存项目类别名称的地方。 那么让我们想象一下股票 table

ID      |   Name   |   Price    | CategoryID
_____________________________________________
5465814 |Item1     | 2.00       |  1         
5465815 |Item2     | 2.00       |  2         
5465816 |Item3     | 2.00       |  1/1         
5465817 |Item4     | 2.00       |  1/3/5         
5465818 |Item5     | 2.00       |  4            
5465818 |Item5     | 2.00       |  4/1            
5465818 |Item5     | 2.00       |  4/2/7               

那么类别 table 是这样的:

CategoryID|   Name   
_____________________________________________
1         |Tools
1/1       |Manual
1/2       |Electrical
1/3       |Pneumatic
1/3/5     |Saws
2         |Chairs
4         |Toys
4/1       |for Girls
4/1/1     |Dolls
4/2       |for Boys
4/2/1     |Cars
4/2/7     |Action Figures

我想不通的是如何在将此 ID 加入 select 时转换为组合类别名称,例如将 4/2/7 转换为: Toys > for Boys > Action Figures

我最终想要完成的是得到一个像这样的 table:

ID      |   Name   |   Price    | Category
_____________________________________________
5465814 |Item1     | 2.00       |  Tools         
5465815 |Item2     | 2.00       |  Chairs         
5465816 |Item3     | 2.00       |  Tools > Manual        
5465817 |Item4     | 2.00       |  Tools > Pneumatic > Saws
5465818 |Item5     | 2.00       |  Toys            
5465818 |Item5     | 2.00       |  Toys > for Girls    
5465818 |Item5     | 2.00       |  Toys > for Boys > Action Figures

这是我目前得到的结果,它最多可以使用 4 个嵌套类别名称:

CASE    
            WHEN len(sd.CategoriyID) - len(replace(sd.CategoriyID,'/','')) = 0 
                THEN ISNULL((SELECT Name FROM Categories WHERE ID = sd.CategoriyID), '')
            WHEN len(sd.CategoriyID) - len(replace(sd.CategoriyID,'/','')) = 1
                THEN 
                    ISNULL((SELECT Name FROM Categories WHERE ID = 
                    SUBSTRING(sd.CategoriyID, 1 , dbo.CHARINDEX2('/', sd.CategoriyID, 1) - 1)), '') 
                    + ' > ' 
                    + ISNULL((SELECT Name FROM Categories WHERE ID = sd.CategoriyID), '')
            WHEN len(sd.CategoriyID) - len(replace(sd.CategoriyID,'/','')) = 2
                THEN 
                    ISNULL((SELECT Name FROM Categories WHERE ID = 
                    SUBSTRING(sd.CategoriyID, 1 , dbo.CHARINDEX2('/', sd.CategoriyID, 2) - 1)), '') 
                    + ' > ' 
                    +ISNULL((SELECT Name FROM Categories WHERE ID = 
                    SUBSTRING(sd.CategoriyID, 1 , dbo.CHARINDEX2('/', sd.CategoriyID, 1) - 1)), '') 
                    + ' > ' 
                    + ISNULL((SELECT Name FROM Categories WHERE ID = sd.CategoriyID), '')
            WHEN len(sd.CategoriyID) - len(replace(sd.CategoriyID,'/','')) = 2
                THEN 
                    ISNULL((SELECT Name FROM Categories WHERE ID = 
                    SUBSTRING(sd.CategoriyID, 1 , dbo.CHARINDEX2('/', sd.CategoriyID, 3) - 1)), '') 
                    + ' > ' 
                    +ISNULL((SELECT Name FROM Categories WHERE ID = 
                    SUBSTRING(sd.CategoriyID, 1 , dbo.CHARINDEX2('/', sd.CategoriyID, 2) - 1)), '') 
                    + ' > ' 
                    +ISNULL((SELECT Name FROM Categories WHERE ID = 
                    SUBSTRING(sd.CategoriyID, 1 , dbo.CHARINDEX2('/', sd.CategoriyID, 1) - 1)), '') 
                    + ' > ' 
                    + ISNULL((SELECT Name FROM Categories WHERE ID = sd.CategoriyID), '')
        END AS Category

SQL 服务器不支持将字符串 agg 作为 window 函数用于累积 windows。但是,您可以改用 apply。因此,使用以下方法从第二个 table 获取全名:

select c.*, v.category
from category c cross apply
     (values (stuff( (select ' > ' + coalesce(c2.name, '')
                      from category c2
                      where c.categoryId + '/' like c2.categoryId + '/%'
                      order by c2.categoryId
                      for xml path (''), type) .value('.', 'nvarchar(max)'
                     ), 1, 3, ''
                   )
             )
     ) v(category);

Here 是这部分的 db<>fiddle。

然后您可以 join 此查询:

with c as (
      select c.*, v.category
      from category c cross apply
           (values (stuff( (select ' > ' + coalesce(c2.name, '')
                            from category c2
                            where c.categoryId + '/' like c2.categoryId + '/%'
                            order by c2.categoryId
                            for xml path (''), type) .value('.', 'nvarchar(max)'
                           ), 1, 3, ''
                         )
                   )
           ) v(category);
      )
select s.*, c.category
from stock s join
     c
     on s.categoryId = c.categoryId;