【问题标题】:get all nested children for a parent id获取父 ID 的所有嵌套子级
【发布时间】:2013-09-03 21:37:18
【问题描述】:

Started Fiddling

Work Table
ProductId, LabelName, CategoryId, ChildCategoryId
------------------------------------
1, Widget A, 1, null
null, Category A, 2, 1 
2, Widget B, 3, null

Categories Table
CategoryId, CategoryName
---------------------------
1, Category A
2, Category B
3, Category C

鉴于以上信息,您将如何获取产品 ID 的所有类别?

例如,给定产品 id 为 1,以下将是所需的结果。

Desired Results
ProductId, LabelName, CategoryId, ChildCategoryId
------------------------------------
1, Widget A, 1, null
null, Category A, 2, 1 
null, Category B, null, 2

它应该是分层数据,我很抱歉无法很好地解释。这简直让我难以置信。小部件 A 的产品 id 为 1,类别 id 为 1。这意味着包含 ChildCategoryId 为 1 的所有记录,这为我们提供了类别 A。CatA 的类别 id 为 2,因此与之前一样,所有具有结果中包含 2 的 ChildCategoryId,这就是包含类别 B 的原因。

【问题讨论】:

  • 您能解释一下您是如何从您提供的样本数据中得出所需结果的吗? associationTable 又从何而来?
  • 为了更清楚一点,我将关联表重命名为事务表。可能没有工作。它不是主表或连接表。我猜这就像一个结果表。
  • Category B 通过Category A 与Widget A 相关。Transaction 表中的第二条记录显示Category A 的category id 为2。
  • 我还是没听懂。您的评论说“类别 A 的类别 ID 为 2”。 Categories 说“A 类”的 CategoryId 为 1。TransactionTable 恰好有一行与 LabelName“A 类”(以前为“类别 1”。)和 CategoryId 为 2,但它是显然,与“Widget A”行没有任何关系。您不会假设表中行的顺序很重要,是吗?如果是这样,您需要提供明确的顺序。您可以编辑您的问题以包含一个简单的分步程序吗?这将使编写 CTE 变得更加容易。
  • 行的顺序不重要。这是我所说的令人困惑的部分。在第二条记录 TransactionTable 中,它表示类别 A,但是,类别 id 是指与其链接的子记录。让我尝试以某种方式改写 OP

标签: sql sql-server-2008 tsql


【解决方案1】:

这种混乱会从样本数据中产生样本结果。目前还不清楚认为算法应该是什么。

declare @CategoryItems as Table (
  CategoryName NVarChar(255),
  Label NVarChar(255),
  ProductId Int,
  ChildCategoryId Int,
  CategoryId Int );

declare @Categories as Table (
  CategoryId Int,
  Name NVarChar(100) );

insert into @CategoryItems ( CategoryName, Label, ProductId, ChildCategoryId, CategoryId ) values
  ( 'CategoryA', 'Widget A', 1, 0, 1 ),
  ( 'CategoryB', 'CategoryA', 0, 1, 2 ),
  ( 'CategoryC', 'Widget B', 2, 0, 3 );
insert into @Categories ( CategoryId, Name ) values
  ( 1, 'CategoryA' ),
  ( 2, 'CategoryB' ),
  ( 3, 'CategoryC' );

select * from @Categories;
select * from @CategoryItems;

declare @TargetProductId as Int = 1;

with Leonard as (
  -- Start with the target product.
  select 1 as [Row], ProductId, Label, CategoryId, ChildCategoryId
    from @CategoryItems
    where ProductId = @TargetProductId
  union all
  -- Add each level of child category.
  select L.Row + 1, NULL, CI.Label, CI.CategoryId, CI.ChildCategoryId
    from @CategoryItems as CI inner join
      Leonard as L on L.CategoryId = CI.ChildCategoryId ),
  Gertrude as (
    -- Take everything that makes sense.
    select Row, ProductId, Label, CategoryId, ChildCategoryId
      from Leonard
    union
    -- Then tack on an extra row for good measure.
    select L.Row + 1, NULL, C.Name, NULL, C.CategoryId
      from Leonard as L inner join
        @Categories as C on C.CategoryId = L.CategoryId
      where L.Row = ( select Max( Row ) from Leonard ) )
  select Row, ProductId, Label, CategoryId, ChildCategoryId
    from Gertrude
    order by Row;

我怀疑问题在于您以不平衡的方式混合了数据。类别的层次结构通常表示为:

declare @Categories as Table (
  CategoryId Int Identity,
  Category NVarChar(128),
  ParentCategoryId Int Null );

每个层次结构的根由ParentCategoryId is NULL 表示。这允许任意数量的独立树共存于一个表中,并且不依赖于任何产品的存在。

如果产品被分配到一个(子)类别,那么只需在Products 表中包含CategoryId。如果一个产品可能被分配到几个(子)类别,可能在不同的层次结构中,那么使用单独的表来关联它们:

declare @ProductCategories as Table (
  ProductId Int,
  CategoryId Int );

【讨论】:

  • 任何人都知道资源链接或示例非 cte 递归 SQL 查询
  • @Rod - AFAIK,在 TSQL 中实现 递归查询 的唯一方法是使用 CTE。您可以使用循环和临时表、游标可选或可能使用表值函数来实现 recursion。为什么要问?
猜你喜欢
  • 2018-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 1970-01-01
  • 2021-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多