如果销售包含产品且产品可以分为多个类别,则必须遵循表格。因此,类别具有类似的结构:
Man
Shoes
Sport
Casual
Watches
Women
Shoes
Sport
Casual
Watches
表:
Sale:
id name
1 Sale1
Product:
id saleidfk name
1 1 a
2 1 b
3 1 c
4 1 d
5 1 e
ProductCategory :
productid categoryid
1 3
2 3
3 4
4 5
5 10
Category:
id ParentCategoryIdFk name
1 null Men
2 1 Shoes
3 2 Sport
4 2 Casual
5 1 Watches
6 null Women
7 6 Shoes
8 7 Sport
9 7 Casual
10 6 Watches
问题:
现在,在我的网站上,我想要创建一个控件,其中只显示特定销售的类别,并且类别中的所有类别都包含销售产品。我也想要 包括类别的层次结构。因此,如果我们有一个离开节点,请重新调整到顶级节点。
因此,对于sale1,我应该有一个查询,结果如下:
Men
Shoes
Sport
Casual
Watches
Women
Watches
这件事让我发疯: - )
提前致谢!
的Gr
马亭
答案 0 :(得分:4)
尝试这样的事情 - 获得类别的分层列表的基本CTE将与此类似:
WITH Categories AS
(
SELECT Cat.ID, Cat.NAME, Cat.ParentCategoryID, CAST('none' AS VARCHAR(50)) AS 'ParentCategory', 1 AS 'Level'
FROM dbo.MBCategory Cat
WHERE Cat.ParentCategoryID IS NULL
UNION ALL
SELECT Cat.ID, Cat.NAME, Cat.ParentCategoryID, c2.NAME AS 'ParentCategory', LEVEL + 1
FROM dbo.MBCategory CAT
INNER JOIN Categories c2 ON cat.ParentCategoryID = c2.ID
)
SELECT * FROM Categories
现在您需要做的是将其他表连接到此CTE,最后得到以下查询:
WITH Categories AS
(
SELECT Cat.ID, Cat.NAME, Cat.ParentCategoryID, CAST('none' AS VARCHAR(50)) AS 'ParentCategory', 1 AS 'Level'
FROM dbo.MBCategory Cat
WHERE Cat.ParentCategoryID IS NULL
UNION ALL
SELECT Cat.ID, Cat.NAME, Cat.ParentCategoryID, c2.NAME AS 'ParentCategory', LEVEL + 1
FROM dbo.MBCategory CAT
INNER JOIN Categories c2 ON cat.ParentCategoryID = c2.ID
)
SELECT DISTINCT s.*, c.*
FROM dbo.Sale s
INNER JOIN dbo.Product p ON p.SaleID = s.ID
INNER JOIN dbo.ProductCategory pc ON p.ID = pc.ProductID
INNER JOIN Categories c ON pc.CategoryID = c.ID
ORDER BY Level
这给了我一个结果输出:
ID Name CatID CatName ParentCatID ParentCatName Level
1 Sale1 5 Watches 1 Men 2
1 Sale1 10 Watches 6 Women 2
1 Sale1 3 Sport 2 Shoes 3
1 Sale1 3 Sport 2 Shoes 3
1 Sale1 4 Casual 2 Shoes 3
答案 1 :(得分:1)
如果您创建一个列出每个类别的所有祖先类别(父级,祖父级等)的附加表,我认为您将获得最快的性能以及更清晰的SQL查询,如下所示:
CategoryAncestor
ID categoryid ancestorid
1 1 1 -- Men, obligatory self reference (makes queries easier)
2 2 2 -- Shoes, self reference
3 2 1 -- Shoes is a subcategory of Men
4 3 3 -- Sport, self reference
5 3 2 -- Sport is a subcategory of Shoes
6 3 1 -- Sport is ALSO a subcategory of Men
-- etc.
当您插入新类别或删除它们时,这会产生更多的SQL开销,但会让您更快地运行查询。
您可能要考虑的下一件事是在类别中添加排名和级别列(同样,在创建和删除类别时更多工作):
id ParentCategoryIdFk name level rank
1 null Men 0 1
2 1 Shoes 1 2
3 2 Sport 2 3
4 2 Casual 2 4
5 1 Watches 1 5
6 null Women 0 6
7 6 Shoes 1 7
8 7 Sport 2 8
9 7 Casual 2 9
10 6 Watches 1 10
排名列指定排序顺序。
然后,您只需运行以下查询:
SELECT * FROM Category c
WHERE c.id IN (
SELECT ancestorid FROM CategoryAncestor ca, ProductCategory pc, Product p
WHERE p.id = pc.productid
AND pc.categoryid = ca.categoryid
AND p.saleidfk = 1
)
ORDER BY rank
希望这有帮助。
答案 2 :(得分:1)
它不是特别有效,但如果您想要做的是有效地“爆炸”整个层次结构并按顺序从父到叶获得结果,这样的事情就会这样做:
WITH CategoryHierarchy AS
(
SELECT
ID, ParentCategoryIdFk, 0 AS Level,
ROW_NUMBER() OVER (ORDER BY ID) AS SubTreeID
FROM Category
WHERE CategoryID IN
(
SELECT pc.CategoryID
FROM Sale s
INNER JOIN Product p
ON p.saleidfk = s.id
INNER JOIN ProductCategory pc
ON pc.productid = p.id
WHERE s.id = @SaleID
)
UNION ALL
SELECT c.ID, c.ParentCategoryIdFk, h.Level + 1, h.SubTreeID
FROM CategoryHierarchy h
INNER JOIN Category c
ON c.ID = h.ParentID
)
SELECT c.ID, c.ParentCategoryIdFk AS ParentID, c.Name
FROM CategoryHierarchy h
INNER JOIN Category c
ON c.ID = h.ID
ORDER BY h.SubTreeID ASC, h.Level DESC
这可以获得类似于以下内容的结果:
ID | ParentID | Name
---+----------+----------
1 | NULL | Men
2 | 1 | Shoes
3 | 2 | Sport
---+----------+----------
1 | NULL | Men
2 | 1 | Shoes
4 | 2 | Casual
---+----------+----------
1 | NULL | Men
5 | 1 | Watches
---+----------+----------
6 | NULL | Women
10 | 6 | Watches
当然实际结果不会有这样的分隔符,我添加了这些分隔符以使结果的含义更清晰。
如果您不希望它像这样完全爆炸,您可以使用另一个rownum仅返回每个父级的第一个实例:
WITH CategoryHierarchy AS
(
SELECT
ID, ParentCategoryIdFk, 0 AS Level,
ROW_NUMBER() OVER (ORDER BY ID) AS SubTreeID
FROM Category
WHERE CategoryID IN
(
SELECT pc.CategoryID
FROM Sale s
INNER JOIN Product p
ON p.saleidfk = s.id
INNER JOIN ProductCategory pc
ON pc.productid = p.id
WHERE s.id = @SaleID
)
UNION ALL
SELECT c.ID, c.ParentCategoryIdFk, h.Level + 1, h.SubTreeID
FROM CategoryHierarchy h
INNER JOIN Category c
ON c.ID = h.ParentID
),
Filter_CTE AS
(
SELECT
ID, Level, SubTreeID
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY SubTreeID) AS RowNum
FROM CategoryHierarchy
)
SELECT c.ID, c.ParentCategoryIdFk AS ParentID, c.Name
FROM Filter_CTE f
INNER JOIN Category c
ON c.ID = f.ID
WHERE f.RowNum = 1
ORDER BY f.SubTreeID ASC, f.Level DESC
...会给你类似的结果:
ID | ParentID | Name
---+----------+----------
1 | NULL | Men
2 | 1 | Shoes
3 | 2 | Sport
4 | 2 | Casual
5 | 1 | Watches
6 | NULL | Women
10 | 6 | Watches
注意:请注意第二个版本,因为不一定保证按层次顺序返回结果。恰好这个版本的确如此,因为ID本身是按层次顺序排列的。你可以解决这个限制,但这会给这个已经有点复杂的查询增加更多的复杂性。
第二个版本确实保证主类别将始终出现在其任何子类别之前,如果您打算使用字典构建递归数据结构,这很好。它可能不适合更快的基于堆栈的树构建或直接用户报告。出于这些目的,您可能希望使用第一个版本。
答案 3 :(得分:0)
有点乱,但是:
DROP TABLE #Sale
GO
DROP TABLE #PRoduct
GO
DROP TABLE #ProductCategory
GO
DROP TABLE #Category
GO
CREATE TABLE #Sale
(
ID INT,
Name VARCHAR(20)
)
GO
INSERT INTO #Sale SELECT 1, 'Sale1'
GO
CREATE TABLE #Product
(
ID INT,
saleidfk INT,
name VARCHAR(20)
)
GO
INSERT INTO #Product
SELECT 1,1,'a'
UNION
SELECT 2,1,'b'
UNION
SELECT 3,1,'c'
UNION
SELECT 4,1,'d'
UNION
SELECT 5,1,'e'
UNION
SELECT 6,1,'f'
GO
CREATE TABLE #ProductCategory
(
ProductID INT,
CategoryID INT
)
GO
INSERT INTO #ProductCategory
SELECT 1,3
UNION
SELECT 2,3
UNION
SELECT 3,4
UNION
SELECT 4,5
UNION
SELECT 5,10
UNION
SELECT 6,10
GO
CREATE TABLE #Category
(
ID INT,
ParentCategoryFK INT,
Name varchar(20)
)
GO
INSERT INTO #Category
SELECT 1,NULL,'Men'
UNION
SELECT 2,1,'Shoes'
UNION
SELECT 3,2,'Sport'
UNION
SELECT 4,2,'Casual'
UNION
SELECT 5,1,'Watches'
UNION
SELECT 6,NULL,'Women'
UNION
SELECT 7,6,'Shoes'
UNION
SELECT 8,7,'Sport'
UNION
SELECT 9,7,'Casual'
UNION
SELECT 10,6,'Watches'
GO
WITH Categories (CategoryName,CategoryID, [Level], SortOrder) AS
(
SELECT Cat.Name,cat.id, 1 AS [Level], CONVERT(VARCHAR(MAX), ROW_NUMBER() OVER (order by cat.Name) ) AS SortOrder
FROM #Category Cat
WHERE Cat.ParentCategoryFK IS NULL
UNION ALL
SELECT CAT.Name,cat.ID, [Level] + 1, c2.SortOrder + CONVERT(VARCHAR(MAX), ROW_NUMBER() OVER (order by cat.Name))
FROM #Category CAT
INNER JOIN Categories c2 ON cat.ParentCategoryFK = c2.CategoryID
)
SELECT #Sale.Name, Categories.CategoryName, #Product.name,Categories.Level,Categories.SortOrder FROM
Categories
LEFT JOIN
#ProductCategory ON #ProductCategory.CategoryID = Categories.CategoryID
LEFT JOIN
#Product ON #Product.ID = #ProductCategory.ProductID
LEFT JOIN
#Sale ON #Product.saleidfk = #Sale.ID
ORDER BY Categories.SortOrder, #Product.name
要注意的相关要点是,要使整个层次结构有意义,您需要类别是否有产品。此外,SortOrder的varchar允许层次结构以正确的顺序显示。
答案 4 :(得分:0)
我想我已经太迟了,但是对于未来的同行来说,我认为这样做会有效。 :)(刚刚为一个特定项目的父层次结构做了,但是带叶子的内部联接将执行相同的操作)
with
hierarchy (id, parentId, level)
as
(
select c.id, c.parentId, 0 as level
from categories c
where parentId = 0
union all
select c.id, c.parentId, level + 1
from categories c
inner join hierarchy p on c.parentId = p.id
),
parents (id, parentId, level)
as
(
select l.id, l.parentId, l.level
from hierarchy l
[where id = *leafid* | inner join *insert_your_leaves_here*]
union all
select p.id, p.parentId, p.level
from hierarchy p
inner join parents l on p.id = l.parentId
)
select * from parents