Ted*_*erz 13 sql database sql-server sql-server-2008
假设我有表A和表B.表B引用表A.我想深度复制表A和表B中的一组行.我希望所有新的表B行引用新的表A行.
请注意,我没有将行复制到任何其他表中.表A中的行将被复制到表A中,表B中的行将被复制到表B中.
如何确保外键引用作为副本的一部分重新调整?
为了澄清,我试图找到一种通用的方法来做到这一点.我给出的示例涉及两个表,但实际上依赖图可能要复杂得多.即使是动态生成SQL来完成工作的通用方法也没问题.
更新:
人们都在问为什么这是必要的,所以我会给出一些背景知识.这可能太过分了,但是这里有:
我正在使用已转移到客户端 - 服务器模型的旧桌面应用程序.但是,该应用程序仍然使用基本的内部二进制文件格式来存储其表的数据.数据文件只是一个标题,后跟一系列行,每个行只是二进制序列化字段值,其顺序由模式文本文件确定.它唯一的好处是它非常快.在其他各方面都很可怕.我正在将应用程序移动到SQL Server并尝试不要太糟糕地降低性能.
这是一种调度应用程序; 数据对任何人都不重要,也没有必要的审计跟踪等.它不是超大量的数据,如果数据库变得太大,我们不一定需要保留非常旧的数据.
他们习惯的一个功能是能够复制整个计划,以创建他们可以搞砸的"假设"场景.任何用户都可以根据需要多次执行此操作.在旧数据库中,每个计划的数据文件存储在它们自己的数据文件夹中,由名称标识.因此,复制计划就像复制数据文件夹并重命名一样简单.
我必须能够与SQL Server有效地做同样的事情,否则迁移将无法正常工作.也许你认为我只能复制实际变化的数据以避免冗余; 但老实说听起来太复杂了,不可行.
要将另一个扳手投入混合,可以有一个计划数据文件夹的层次结构.因此,数据文件夹可能包含数据文件夹,该文件夹可能包含数据文件夹.并且复制可以在任何级别进行.
在SQL Server中,我正在实现嵌套集层次结构来模仿它.我有一个像这样的DATA_SET表:
CREATE TABLE dbo.DATA_SET
(
DATA_SET_ID UNIQUEIDENTIFIER PRIMARY KEY,
NAME NVARCHAR(128) NOT NULL,
LFT INT NOT NULL,
RGT INT NOT NULL
)
Run Code Online (Sandbox Code Playgroud)
所以,有一个数据集的树结构.每个数据集代表一个计划,可能包含子数据集.每个表中的每一行都有一个DATA_SET_ID FK引用,指示它属于哪个数据集.每当我复制一个数据集时,我都会将该数据集的表中的所有行以及所有其他数据集复制到同一个表中,但会引用新的数据集.
所以,这是一个简单的具体例子:
CREATE TABLE FOO
(
FOO_ID BIGINT PRIMARY KEY,
DATA_SET_ID BIGINT FOREIGN KEY REFERENCES DATA_SET(DATA_SET_ID) NOT NULL
)
CREATE TABLE BAR
(
BAR_ID BIGINT PRIMARY KEY,
DATA_SET_ID BIGINT FOREIGN KEY REFERENCES DATA_SET(DATA_SET_ID) NOT NULL,
FOO_ID UNIQUEIDENTIFIER PRIMARY KEY
)
INSERT INTO FOO
SELECT 1, 1 UNION ALL
SELECT 2, 1 UNION ALL
SELECT 3, 1 UNION ALL
INSERT INTO BAR
SELECT 1, 1, 1
SELECT 2, 1, 2
SELECT 3, 1, 3
Run Code Online (Sandbox Code Playgroud)
所以,假设我将数据集1复制到ID 2的新数据集中.复制后,表格如下所示:
FOO
FOO_ID, DATA_SET_ID
1 1
2 1
3 1
4 2
5 2
6 2
BAR
BAR_ID, DATA_SET_ID, FOO_ID
1 1 1
2 1 2
3 1 3
4 2 4
5 2 5
6 2 6
Run Code Online (Sandbox Code Playgroud)
如您所见,新的BAR行引用了新的FOO行.这不是我要问的DATA_SET_ID的重新布线.我问的是一般情况下重新连接外键.
所以,这肯定是太多的信息,但你去了.
我确信有很多关于性能的担忧,想要批量复制这样的数据.表格不会很大.我不希望任何表格中有超过1000条记录,而且大多数表格都要小得多.旧数据集可以直接删除而不会产生任何影响.
谢谢,Tedderz
这是一个包含三个表的示例,可能可以帮助您入门。
数据库架构
CREATE TABLE users
(user_id int auto_increment PRIMARY KEY,
user_name varchar(32));
CREATE TABLE agenda
(agenda_id int auto_increment PRIMARY KEY,
`user_id` int, `agenda_name` varchar(7));
CREATE TABLE events
(event_id int auto_increment PRIMARY KEY,
`agenda_id` int,
`event_name` varchar(8));
Run Code Online (Sandbox Code Playgroud)
一个 SP,用于克隆用户及其议程和事件记录
DELIMITER $$
CREATE PROCEDURE clone_user(IN uid INT)
BEGIN
DECLARE last_user_id INT DEFAULT 0;
INSERT INTO users (user_name)
SELECT user_name
FROM users
WHERE user_id = uid;
SET last_user_id = LAST_INSERT_ID();
INSERT INTO agenda (user_id, agenda_name)
SELECT last_user_id, agenda_name
FROM agenda
WHERE user_id = uid;
INSERT INTO events (agenda_id, event_name)
SELECT a3.agenda_id_new, e.event_name
FROM events e JOIN
(SELECT a1.agenda_id agenda_id_old,
a2.agenda_id agenda_id_new
FROM
(SELECT agenda_id, @n := @n + 1 n
FROM agenda, (SELECT @n := 0) n
WHERE user_id = uid
ORDER BY agenda_id) a1 JOIN
(SELECT agenda_id, @m := @m + 1 m
FROM agenda, (SELECT @m := 0) m
WHERE user_id = last_user_id
ORDER BY agenda_id) a2 ON a1.n = a2.m) a3
ON e.agenda_id = a3.agenda_id_old;
END$$
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)
克隆用户
CALL clone_user(3);
Run Code Online (Sandbox Code Playgroud)
这是SQLFiddle演示。