SQL - 循环 - 复制类别

发布于 2024-08-10 18:26:39 字数 203 浏览 6 评论 0 原文

假设类别表有categoryId、name 和parentCategoryId 列,其中categoryId 是标识字段。

现在,如果我必须复制一个类别并将其分配给新的父级怎么办?因为这不是一个简单的插入语句。我必须插入所有子类别及其子类别等等。我如何跟踪他们的身份字段?我需要在插入时将parentCategoryId 分配给它们的子类别。

这个问题清楚吗?

Assume Category table has categoryId, name and parentCategoryId columns, where categoryId is an identity field.

now what if I have to replicate a category and assign it to a new parent? Because it's not a simple insert statement. I have to insert all the subcategories and their subcategories and so on. How would I keep track of their identity fields? I would need that to assign parentCategoryId to their subcategories when inserting.

Is this question clear?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

走过海棠暮 2024-08-17 18:26:39

不确定您是问 2 个问题还是仅问 1 个问题,即将整个类别复制为新类别(即复制类别)还是将现有类别重新分配给新的父类别 - 每个问题/解决方案都不同,但是让我们从复制整个类别开始。

首先,如果您使用基于身份的列,则在不使用“set Identity_insert on”选项的情况下,唯一的方法是将光标从根节点开始遍历整个树并向下工作(即插入顶级类别,获取新创建的标识值,插入第二级类别等)。如果您处于可以使用“set Identity_insert on”的场景,或者可以用显式数字替换身份的使用,那么您可以利用下面的代码。

在此代码中,您会注意到使用了 CTE、< a href="http://msdn.microsoft.com/en-us/library/ms186243.aspx" rel="nofollow noreferrer">递归 CTE 和 排名函数,因此假定 Sql 2005 或更高版本。此外,lvl、path 和 cnt 列仅出于演示目的而包含,您可以根据需要查看,但在任何最终解决方案中都不需要:

declare @root_category_id bigint,
        @start_new_id_value bigint;

-- What category id do we want to move?
select  @root_category_id = 3;
-- Get the current max id and pad a bit...
select  @start_new_id_value = max(categoryId)
from    Category;
select  @start_new_id_value = coalesce(@start_new_id_value,0) + 100;

-- Show our values
select  @root_category_id, @start_new_id_value;

begin tran;
set identity_insert Category on;

-- This query will give you the entire category tree
with subs (catId, parentCatId, catName, lvl, path, new_id, new_parent_id, cnt) as (
    -- Anchor member returns a row for the input manager
    select  catId, parentCatId, catName, 0 as lvl,
            cast(cast(catId as varchar(10)) as varchar(max)) as path,
            @start_new_id_value + row_number() over(order by catId) - 1 as new_id, 
            cast(parentCatId as bigint) as new_parent_id, 
            count(*) over(partition by 0) as cnt
    from    Category
    where   catId = @root_category_id

    union all

    -- recursive member returns next level of children
    select  c.catId, c.parentCatId, c.catName, p.lvl + 1,
            cast(p.path + '.' + cast(catId as varchar(10)) as varchar(max)),
            p.cnt + row_number() over(order by c.catId) + p.new_id - 1 as new_id,
            p.new_id as new_parent_id,
            count(*) over(partition by p.lvl) as cnt
    from    subs as p -- Parent
    join    Category as c -- Child
    on      c.parentCatId = p.catId
)
-- Perform the insert
insert  Category 
        (categoryId, Name, parentCategoryId)
select  s.catId, s.catName, s.parentCatId
from    subs s
--order by path;

set identity_insert Category off;
commit tran;

Not sure if you're asking 2 questions or just 1, i.e. replicating an entire category as a new category (i.e. copying a category) vs. re-assigning an existing category to a new parent - each would be different problems/solutions, but let's start with copying an entire category.

First, if you're using an identity-based column, the ONLY way you can do it without using the "set identity_insert on" option would be to cursor through the entire tree, starting from the root nodes and working down (i.e. insert the top-level category(ies), get the newly created identity values, insert the second-level categories, etc.). If you are in a scenario where you can make use of "set identity_insert on", or if you can replace the use of identities with explicit numbers, then you can leverage the code below.

In this code, you'll notice the use of CTE's, recursive CTE's, and ranking functions, so this assumes Sql 2005 or above. Also, the lvl, path, and cnt columns are simply included for demo purposes you can use to view if you like, not required in any final solution:

declare @root_category_id bigint,
        @start_new_id_value bigint;

-- What category id do we want to move?
select  @root_category_id = 3;
-- Get the current max id and pad a bit...
select  @start_new_id_value = max(categoryId)
from    Category;
select  @start_new_id_value = coalesce(@start_new_id_value,0) + 100;

-- Show our values
select  @root_category_id, @start_new_id_value;

begin tran;
set identity_insert Category on;

-- This query will give you the entire category tree
with subs (catId, parentCatId, catName, lvl, path, new_id, new_parent_id, cnt) as (
    -- Anchor member returns a row for the input manager
    select  catId, parentCatId, catName, 0 as lvl,
            cast(cast(catId as varchar(10)) as varchar(max)) as path,
            @start_new_id_value + row_number() over(order by catId) - 1 as new_id, 
            cast(parentCatId as bigint) as new_parent_id, 
            count(*) over(partition by 0) as cnt
    from    Category
    where   catId = @root_category_id

    union all

    -- recursive member returns next level of children
    select  c.catId, c.parentCatId, c.catName, p.lvl + 1,
            cast(p.path + '.' + cast(catId as varchar(10)) as varchar(max)),
            p.cnt + row_number() over(order by c.catId) + p.new_id - 1 as new_id,
            p.new_id as new_parent_id,
            count(*) over(partition by p.lvl) as cnt
    from    subs as p -- Parent
    join    Category as c -- Child
    on      c.parentCatId = p.catId
)
-- Perform the insert
insert  Category 
        (categoryId, Name, parentCategoryId)
select  s.catId, s.catName, s.parentCatId
from    subs s
--order by path;

set identity_insert Category off;
commit tran;
李白 2024-08-17 18:26:39

有趣的问题。

如果您使用的是 SQL 2005+,我想您可以构建一个包含要复制的类别的完整树的 CTE,这会将其放置在远离主表的临时位置中进行操作。

然后,您可以使用光标沿着树向下移动并更新父级的 ID 号...

现在我键入它,这似乎不是最有效的解决方案。也许您可以改为执行一个奇特的 SELECT 语句,该语句在执行 SELECT 操作时更新父 ID 的 ID?

Interesting question.

If you're in SQL 2005+, I suppose you could build a CTE with the full tree of the category to replicate, which will put it in a temporary location to work with, away from the main table.

Then you can use a cursor to work your way down the tree and update the ID number of the parent ...

Now that I type it, it doesn't seem the most efficient solution. Perhaps you could instead do a fancy SELECT statement, which updates the ID of the parent ID as it's SELECTing?

叶落知秋 2024-08-17 18:26:39

您知道嵌套集合吗?这是在此类问题中表示数据的另一种方法。我不知道这是否有帮助,但如果您不知道,您可能需要考虑一下。 http://intelligent-enterprise.informationweek.com/001020/celko.jhtml

Are you aware of nested sets? It's an alternative way of representing data in problems like this. I don't know if it would help here, but if you're not aware of it you might want to consider it. http://intelligent-enterprise.informationweek.com/001020/celko.jhtml

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文