4

Предположим, что у нас есть само-ссылочная таблица, как этотКак дублировать строки самостоятельно ссылочной таблицы

CREATE TABLE Month 
(
    Id int IDENTITY(1,1) PRIMARY KEY, 
    Title char(128) 
) 
CREATE TABLE Entity 
(
Id int IDENTITY(1,1) PRIMARY KEY, 
MonthId int FOREIGN KEY REFERENCES Month(Id), 
Name char(128), 
ParentId int FOREIGN KEY REFERENCES Entity(Id), 
) 

Я хочу, чтобы скопировать все строки определенного MonthId другому MonthId. Необходимо также обновить дубликаты parentId, организации и их родители должны быть в том же месяце.

В качестве примера предположим, что мы имеем

Id  MonthId Name  ParentId 
------------------------------------ 
1   1   name1  null 
2   1   name11 1 
3   1   name3  null 
4   1   name31 3 
5   1   name311 4 

после копирования monthId = 1 строк monthId = 2 результат должен быть таким:

Id  MonthId Name  ParentId 
------------------------------------ 
1   1   name1  null 
2   1   name11 1 
3   1   name3  null 
4   1   name31 3 
5   1   name311 4 
newId1 2   name1  null 
newId2 2   name11 newId1 
newId3 2   name3  null 
newId4 2   name31 newId3 
newId5 2   name311 newId4 

В NEWID являются значения, порожденные СУБД.

Примечание: Я использую Sql-Server 2012 как СУБД.

+1

Side Примечание: * пожалуйста * не используйте 'полукокса (128)' 'для столбца name' ! Это сделает ** все ** ваши имена будут длиной до 128 символов - более короткие, заполненные пробелами до определенной длины! Это ужасно плохо, трата пространства - вместо этого используйте «varchar (128)», который использует только столько места, сколько необходимо –

+0

Его просто для примера я использую nvarchar (128) в реальном приложении, так как мне нужны символы Unicode. –

+0

Я могу придумать несколько громоздкий способ вставки записей во временную таблицу со старыми идентификаторами и старыми родительскими идентификаторами, а затем вставить их в основную таблицу с правильными значениями, но я уверен, что это можно сделать в более прямо вперед, поэтому я не буду публиковать это как ответ. –

ответ

6

Это прекрасно работает без каких-либо предположений:

DECLARE @baseMonthId int = 1 
DECLARE @newMonthId int = 2 

DECLARE @newRows TABLE(id int, orig_id int) 

MERGE INTO Entity 
USING (
    SELECT Id, Name, ParentId FROM Entity WHERE MonthId = @baseMonthId 
) AS cf 
ON 1 = 0 
WHEN NOT MATCHED THEN 
    INSERT(MonthId, Name, ParentId) Values(@newMonthId, cf.Name, cf.ParentId) 
OUTPUT inserted.Id, cf.Id INTO @newRows(id, orig_id); 

UPDATE Entity 
SET Parentid = 
    ( 
    SELECT 
     nr.id 
    FROM @newRows nr 
     WHERE nr.orig_id = Entity.ParentId 
    ) 
WHERE MonthId = @newMonthId; 

Результат:

enter image description here

0

Если мы можем рассчитывать на Entity.Name быть уникальным для данного Entity.MonthId, то это может быть сделано в операторах SQL 2:

SQLFiddle

-- copy records, but don't set the ParentId yet. 
INSERT INTO Entity (MonthId, Name, ParentId) 
SELECT 2, Name, null 
FROM Entity 
WHERE MonthId = 1; 

-- set the ParentId in the 2nd step. 
UPDATE e 
SET e.ParentId = (
    SELECT parentNew.Id 
    FROM Entity innerOld 
    JOIN Entity parentOld 
    ON parentOld.Id = innerOld.ParentId 
    JOIN Entity parentNew 
    ON parentNew.MonthId = e.MonthId 
    AND parentNew.Name = parentOld.Name 
    WHERE innerOld.MonthId = 1 
    AND innerOld.Name = e.Name 
) 
FROM Entity e 
WHERE e.MonthId = 2; 

Одним из преимуществ такого подхода заключается в том, что она не сделайте любые предположения о том, как генерируются значения Id.

Смежные вопросы