我正在使用一个非常复杂的查询,其中一部分必须检索与产品关联的类别。类别以递归方式存储在Category
表中。产品类别映射位于ProductCategory
表中(从技术上讲,单个产品可以有多个类别,但是现在让我们将其从表中删除,除非它是一个简单的变量来考虑)。
Category
表非常简单。一列为CategoryID
,另一列为ParentCategoryID
,第三列为Name
列。从这里,类别是嵌套的。 ProductCategory
表也很简单。一列是ProductID
,另一列是CategoryID
。
我需要检索任何给定产品的最顶级和第二顶级类别。然后,我在包含一些分析的报告中使用此信息。我的解决方案非常慢,并且不能很好地扩展。我无法弄清楚如何更有效地提取我需要的数据。
我的解决方案尝试做的是收集作为特定产品指定类别的父项的所有类别,然后抓住我找到的最后两个并返回它们。我已经将其作为标量函数完成,我将当前CategoryID
和我想要的级别发送回来,因此一次调用为0,另一次调用为1。
我的示例代码:
WITH Categories AS (
SELECT DISTINCT
CategoryID
FROM
ProductCategory
), CategoriesAtDepth AS (
SELECT
Categories.CategoryID
, dbo.WR_f_GetCategoryIDAtDepth(Categories.CategoryID, 0) AS TopCategory
, dbo.WR_f_GetCategoryIDAtDepth(Categories.CategoryID, 1) AS SecondCategory
FROM
Categories
)
SELECT
CategoriesAtDepth.CategoryID
, c1.Name AS TopCategory
, c2.Name AS SecondCategory
FROM
CategoriesAtDepth LEFT JOIN
Category AS c1 ON CategoriesAtDepth.TopCategory = c1.CategoryID LEFT JOIN
Category AS c2 ON CategoriesAtDepth.SecondCategory = c2.CategoryID
功能代码:
CREATE FUNCTION WR_f_GetCategoryIDAtDepth
(
@CategoryID AS int
,@Depth AS int = 0
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE @Result int
DECLARE @CurrentHeight int = 0
DECLARE @CurrentCategoryID int = @CategoryID
DECLARE @CategoryLevels table
(
Height int
,CategoryID int
)
BEGIN
--Populate a table with all the categoy IDs in the chain
WHILE @CurrentCategoryID > 0
BEGIN
INSERT INTO @CategoryLevels (Height, CategoryID) VALUES (@CurrentHeight + 1, @CurrentCategoryID)
SET @CurrentCategoryID = (SELECT ParentCategoryID FROM Category WHERE CategoryID = ISNULL((SELECT CategoryID FROM @CategoryLevels WHERE Height = @CurrentHeight + 1), 0))
SET @CurrentHeight = @CurrentHeight + 1
END
SET @Result = (SELECT CategoryID FROM @CategoryLevels WHERE Height = (@CurrentHeight - @Depth))
END
-- Return the result of the function
RETURN @Result
END
GO
我更多地考虑了@George Mavritsakis使用递归CTE的评论,并决定尝试在函数中实现它,并提出了这个很多更快的解决方案:
CREATE FUNCTION WR_f_GetCategoryIDAtDepth
(
@CategoryID AS int
,@Depth AS int = 0
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE @Result int
DECLARE @CategoryLevels table
(
Height int
,CategoryID int
)
BEGIN
--Populate a table with all the categoy IDs in the chain
WITH Base AS (
SELECT
0 AS Height
, @CategoryID AS CategoryID
UNION ALL
SELECT
Height + 1
, ParentCategoryID
FROM
Category INNER JOIN
Base ON Category.CategoryID = Base.CategoryID
)
INSERT INTO @CategoryLevels (Height, CategoryID)
SELECT * FROM Base
SET @Result = (SELECT CategoryID FROM @CategoryLevels WHERE Height = ((SELECT MAX(Height) FROM @CategoryLevels) - @Depth - 1))
END
-- Return the result of the function
RETURN @Result
END
GO
答案 0 :(得分:1)
你必须研究递归CTE:http://technet.microsoft.com/en-us/library/ms186243%28v=sql.105%29.aspx
您的解决方案很慢,因为您使用函数 WR_f_GetCategoryIDAtDepth 多次查询类别表。