You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在SQL Server中用HierarchyID和递归CTE显示完整产品分类路径

借助SQL Server 2019的HierarchyID生成产品主分类完整路径

你已经通过传统ParentID外键实现了分类路径需求,现在要基于HierarchyID数据类型生成每个产品主分类的完整层级路径,以下是具体实现方案:

你的表结构

CREATE TABLE tbl2ProductCategories (
    -- Primary Key Field
    ProductCategoryID INT NOT NULL IDENTITY(1,1),
    -- Non Key Fields
    Node HIERARCHYID NOT NULL UNIQUE,
    ProductCategory NVARCHAR(50) NOT NULL,
    -- Constraints
    CONSTRAINT PK_tbl2ProductCategories PRIMARY KEY (ProductCategoryID)
);

CREATE TABLE tbl1Products (
    -- Primary Key Field
    ProductID INT NOT NULL IDENTITY(1,1),
    -- Non Key Fields
    ProductName NVARCHAR(50) NOT NULL UNIQUE,
    -- Constraints
    CONSTRAINT PK_tbl1Products PRIMARY KEY (ProductID),
);

-- Each product can be in multiple categories
CREATE TABLE tbl3ProductsCategories (
    -- Primary Key Field
    ProductID INT NOT NULL,
    ProductCategoryID INT NOT NULL,
    -- Non Key Fields
    IsPrimaryCategory BIT NOT NULL DEFAULT 1
    -- Constraints
    CONSTRAINT PK_tbl3ProductsCategories PRIMARY KEY (ProductID, ProductCategoryID),
    CONSTRAINT FK_tbl3ProductsCategories_tbl1Products FOREIGN KEY (ProductID) REFERENCES tbl1Products (ProductID),
    CONSTRAINT FK_tbl3ProductsCategories_tbl2ProductCategories FOREIGN KEY (ProductCategoryID) REFERENCES tbl2ProductCategories (ProductCategoryID)
);

实现步骤

1. 确保分类表的HierarchyID层级正确

首先需要保证tbl2ProductCategories中的Node字段正确维护了分类的层级关系,比如根节点为/,子节点为/1/、/1/1/等。可以插入测试数据验证:

-- 插入测试分类数据
INSERT INTO tbl2ProductCategories (Node, ProductCategory)
VALUES
    (HierarchyID::GetRoot(), 'Products'), -- 根节点 /
    (HierarchyID::GetRoot().GetDescendant(NULL, NULL), 'Category1'), -- /1/
    (HierarchyID::Parse('/1/').GetDescendant(NULL, NULL), 'Subcategory1'), -- /1/1/
    (HierarchyID::Parse('/1/1/').GetDescendant(NULL, NULL), 'Subsubcategory1'), -- /1/1/1/
    (HierarchyID::Parse('/1/').GetDescendant(HierarchyID::Parse('/1/1/'), NULL), 'Subcategory3'), -- /1/2/
    (HierarchyID::GetRoot().GetDescendant(HierarchyID::Parse('/1/'), NULL), 'Category5'); -- /2/

-- 插入测试产品
INSERT INTO tbl1Products (ProductName)
VALUES ('Product 1'), ('Product 2'), ('Product 3');

-- 关联主分类
INSERT INTO tbl3ProductsCategories (ProductID, ProductCategoryID, IsPrimaryCategory)
VALUES
    (1, (SELECT ProductCategoryID FROM tbl2ProductCategories WHERE ProductCategory = 'Subsubcategory1'), 1),
    (2, (SELECT ProductCategoryID FROM tbl2ProductCategories WHERE ProductCategory = 'Subcategory3'), 1),
    (3, (SELECT ProductCategoryID FROM tbl2ProductCategories WHERE ProductCategory = 'Category5'), 1);

2. 编写查询生成完整路径

通过递归CTE遍历HierarchyID的层级关系,拼接每个分类的完整路径,再关联产品和主分类数据:

-- 递归CTE获取每个分类的完整路径
WITH CategoryPaths AS (
    -- 锚点成员:根节点
    SELECT
        ProductCategoryID,
        Node,
        ProductCategory,
        CAST(ProductCategory AS NVARCHAR(MAX)) AS FullCategoryPath
    FROM tbl2ProductCategories
    WHERE Node = HierarchyID::GetRoot()

    UNION ALL

    -- 递归成员:子节点拼接父节点路径
    SELECT
        c.ProductCategoryID,
        c.Node,
        c.ProductCategory,
        CAST(cp.FullCategoryPath + ' > ' + c.ProductCategory AS NVARCHAR(MAX)) AS FullCategoryPath
    FROM tbl2ProductCategories c
    JOIN CategoryPaths cp ON c.Node.GetAncestor(1) = cp.Node
)
-- 关联产品和主分类,获取最终结果
SELECT
    p.ProductName + ': ' + cp.FullCategoryPath AS ProductWithCategoryPath
FROM tbl1Products p
JOIN tbl3ProductsCategories pc ON p.ProductID = pc.ProductID
JOIN CategoryPaths cp ON pc.ProductCategoryID = cp.ProductCategoryID
WHERE pc.IsPrimaryCategory = 1
ORDER BY p.ProductID;

结果说明

执行上述查询后,会输出符合需求的结果:

  • Product 1: Products > Category1 > Subcategory1 > Subsubcategory1
  • Product 2: Products > Category1 > Subcategory3
  • Product 3: Products > Category5

对你原有CTE的修正说明

你之前的递归CTE没有关联分类表,也未利用HierarchyID的GetAncestor()方法建立层级关联,导致无法拼接路径。上述方案通过锚点成员定位根分类,递归成员逐层关联父节点并拼接名称,最终结合产品关联数据得到目标结果。

内容的提问来源于stack exchange,提问作者ThomassoCZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 01:15:41