Sql 分层数据的克隆

Sql 分层数据的克隆,sql,database-design,postgresql,Sql,Database Design,Postgresql,让我们假设我有一个自引用的分层表,它是以经典的方式构建的,如下所示: CREATE TABLE test (name text,id serial primary key,parent_id integer references test); insert into test (name,id,parent_id) values ('root1',1,NULL),('root2',2,NULL),('root1sub1',3,1),('root1sub2',4,1),('root 2sub1'

让我们假设我有一个自引用的分层表,它是以经典的方式构建的,如下所示:

CREATE TABLE test
(name text,id serial primary key,parent_id integer
references test);

insert into test (name,id,parent_id) values
('root1',1,NULL),('root2',2,NULL),('root1sub1',3,1),('root1sub2',4,1),('root
2sub1',5,2),('root2sub2',6,2);

testdb=# select * from test;

   name    | id | parent_id
-----------+----+-----------
 root1     |  1 |  
 root2     |  2 |  
 root1sub1 |  3 |         1
 root1sub2 |  4 |         1
 root2sub1 |  5 |         2
 root2sub2 |  6 |         2
我现在需要的是一个函数(最好是纯sql),它将获取测试记录的id并 克隆所有附加记录(包括给定记录)。克隆的记录当然需要有新的ID。期望的结果 我想举个例子:

Select * from cloningfunction(2);

   name    | id | parent_id    
-----------+----+-----------
 root2     |  7 |  
 root2sub1 |  8 |         7
 root2sub2 |  9 |         7

有什么建议吗?我正在使用PostgreSQL 8.3。

递归地提取这个结果是很棘手的(尽管可能)。然而,它通常不是很有效,有更好的方法来解决这个问题

基本上,您可以使用一个额外的列来扩充表,该列将树跟踪到顶部,我将其称为“Upchain”。它只是一个长字符串,看起来像这样:

name | id | parent_id | upchain
root1 | 1 | NULL | 1:
root2 | 2 | NULL | 2:
root1sub1 | 3 | 1 | 1:3:
root1sub2 | 4 | 1 | 1:4:
root2sub1 | 5 | 2 | 2:5:
root2sub2 | 6 | 2 | 2:6:
root1sub1sub1 | 7 | 3 | 1:3:7:
使用表上的触发器可以很容易地更新此字段。(对于术语,我深表歉意,但我一直在使用SQL Server)。每次添加或删除记录或更新父id字段时,只需更新树的该部分上的upchain字段。这是一项微不足道的工作,因为您只需获取父记录的上链并附加当前记录的id。所有子记录都可以使用LIKE轻松识别,以检查其上链中具有起始字符串的记录

有效的做法是,在读取数据时,用一点额外的写入活动换取一大笔节省

当您想要在树中选择一个完整的分支时,这是很简单的。假设您希望分支位于节点1下。节点1具有上链“1:”,因此您知道该节点下的树分支中的任何节点都必须具有以“1:…”开头的上链。所以你只要这样做:

SELECT *
FROM table
WHERE upchain LIKE '1:%'
这是非常快的(当然,索引upchain字段)。作为奖励,它还使许多活动变得极其简单,例如查找部分树、树中的级别等

我在跟踪大型员工报告层次结构的应用程序中使用过它,但您可以将其用于几乎任何树结构(部件分解等)

注意事项(对于感兴趣的人):

  • 我没有给出一步一步的SQL代码,但是一旦你了解了原理,它就很容易实现了。我不是一个优秀的程序员,所以我是凭经验说话的
  • 如果表中已有数据,则需要进行一次性更新,以使上行链最初同步。同样,这并不困难,因为代码与触发器中的更新代码非常相似
  • 这种技术也是识别循环引用的一种好方法,否则很难发现循环引用

这听起来像是Joe Celko的“智能SQL”中的一个练习


我手边没有我的副本,但我认为如果这是你需要解决的问题,这本书会对你有很大帮助。

乔·塞尔科的方法类似于,但更通用,可以在这里找到:

  • (在文章中间)
    • :你说得对,我们忘记了你的实际要求。递归存储过程怎么样?我不确定这在PostgreSQL中是否可行,但这里有一个可用的SQL Server版本:

      CREATE PROCEDURE CloneNode
          @to_clone_id int, @parent_id int
      AS
          SET NOCOUNT ON
          DECLARE @new_node_id int, @child_id int
      
          INSERT INTO test (name, parent_id) 
              SELECT name, @parent_id FROM test WHERE id = @to_clone_id
          SET @new_node_id = @@IDENTITY
      
          DECLARE @children_cursor CURSOR
          SET @children_cursor = CURSOR FOR 
              SELECT id FROM test WHERE parent_id = @to_clone_id
      
          OPEN @children_cursor
          FETCH NEXT FROM @children_cursor INTO @child_id
          WHILE @@FETCH_STATUS = 0
          BEGIN
              EXECUTE CloneNode @child_id, @new_node_id
              FETCH NEXT FROM @children_cursor INTO @child_id
          END
          CLOSE @children_cursor
          DEALLOCATE @children_cursor
      

      您的示例是通过执行CloneNode 2,null来完成的(第二个参数是新的父节点)。

      我认为触发器必须更新层次结构的所有子记录。例如,如果我们将记录“root2”的父\u id更新为1,则还必须更新记录“root2sub1”和“root2sub2”。这是具体化的路径方式。。。确实很方便