我现在几次遇到这个问题,我想找到一个更好的方法来解决这个问题。
基本上我有一个具有子类别的类别。如果子类别没有描述我想检索父类别描述。
当我有Sub Sub类别时,它开始变得困难。
Sql表如下所示:
CategoryID int,
ParentCategoryID int null,
Name varchar(255),
Description varchar(MAX) null
我想创建一个查找Description的函数,但我不知道这是否是最佳解决方案或如何创建函数。
我主要是寻找解决此问题的最佳/正确方法。
答案 0 :(得分:3)
假设:
CategoryId是主键。
如果行级别的描述为空,请查看父行。如果Parent行具有null描述,请查看parent的父级。等等。换句话说,使用祖先的第一个非空描述。
如果行级别描述为空,并且没有祖先存在非空描述,则整体描述为空
设置示例表和测试数据。
create table #SO (CategoryID int primary key
, ParentCategoryID int Null
, Name varchar(255) not null
, Description varchar(MAX) Null
)
insert into #SO (CategoryID, ParentCategoryID, Name, Description)
values (1, null, 'Top 1', 'Top 1 Description')
, (2, null, 'Top 2', 'Top 2 Description')
, (3, null, 'Top 3', null)
, (11, 1, 'Child 11', 'Child 11 Description')
, (12, 1, 'Child 12', null)
, (21, 2, 'Child 21', null)
, (211, 21, 'Child 211', null)
, (2111, 211, 'Child 2111', null)
, (2112, 211, 'Child 2112', 'Child 2112 Description')
, (31, 3, 'Child 31', 'Child 31 Description')
, (32, 3, 'Child 32', null)
使用递归CTE。请注意,树向上走。我们从所有行开始,然后根据需要查看父项,而不是从树的顶部开始正常的树操作并继续工作。
; with Description (BaseCategoryId
, CurrentParentCategoryId
, CurrentDescription
, CurrentLevel)
as
(-- Anchor -- Start with all rows in the table.
select CategoryId as BaseCategoryId
, ParentCategoryId as CurrentParentCategoryId
, Description as CurrentDescription
, 0 as CurrentLevel
from #SO -- Recursive -- We are walking up the tree from all nodes,
-- We only continue up the tree when we do not have a description yet.
union all
select D.BaseCategoryId
, so.ParentCategoryId
, so.Description
, D.CurrentLevel + 1
from #SO so
inner join Description D
on D.CurrentParentCategoryId = so.CategoryId
and D.CurrentDescription is null)
select DL.BaseCategoryId as CategoryId
, DL.CurrentDescription as UltimateDescription
-- Now self outer join with the CTE every step of the walk
-- for each BaseCategoryId, and then filter all but the top
-- level. (Level is measured as distance from base.)
from Description as DL
left outer join Description as DR
on DL.BaseCategoryId = DR.BaseCategoryId
and DL.CurrentLevel < DR.CurrentLevel
where DR.BaseCategoryId is null
order by DL.BaseCategoryId
输出是从CategoryId到最终描述的映射。
在重复使用方面,我会将上述内容视为一种观点。
答案 1 :(得分:1)
鉴于您的结构,Alex Martelli的解决方案可能是您找到的最佳解决方案。如果您可以更改模型,另一个选项是从链接列表树结构更改为嵌套集模型。
Joe Celko有a book on trees and hierarchies,它可以通过各种方式对其进行建模以及每种方法的优缺点。您也可以通过Google或Google网上论坛找到有关该主题的大量信息。
嵌套集模型的一个缺点是对树结构的更改有点贵,但对于产品,您通常检索的内容远远超过更新。特别是在类别之间移动,这在大多数商业案例中通常很少见。
使用嵌套集模型,以下内容可以为您提供所需内容:
SELECT
P1.Name,
COALESCE(P1.Description, P2.Description) AS Description
FROM
Products P1
LEFT OUTER JOIN Products P2 ON
P2.lft < P1.lft AND
P2.rgt > P1.rgt AND
P2.Description IS NOT NULL
LEFT OUTER JOIN Products P3 ON
P3.lft < P1.lft AND P3.lft > P2.lft AND
P3.rgt > P1.rgt AND P3.rgt < P2.rgt AND
P3.Description IS NOT NULL
WHERE
P3.ID IS NULL
答案 2 :(得分:1)
如果您需要递归地遍历父树,请参阅answer from Alex。如果您只需要一个级别,那么简单的LEFT JOIN
应该有效:
SELECT c.CategoryID,
c.ParentCategoryID,
c.Name,
COALESCE(c.Description, p.Description) AS Description
FROM dbo.Category c
LEFT JOIN dbo.Category p
ON c.ParentCategoryID = p.CategoryID
答案 3 :(得分:0)
显然,您需要两个表引用和一个连接。这将使您可以访问这两个描述。然后,您需要一个inline if
语句来选择要使用的值,基于第一个为null。
这样的东西应该有用(表名为Table1)
SELECT Table1.CategoryID, IIf(Parent.Description Is Null, Table1.description, parent.description) AS [Desc]
FROM Table1 INNER JOIN Table1 AS Parent ON Table1.CategoryID = Parent.ParentCategoryID;