2

Я сценарий (таблица), как это:Рекурсивный в SQL Server 2008

enter image description here

Это таблица (Папка) структуру. У меня только записи для user_id = 1 в этой таблице. Теперь мне нужно вставить ту же структуру папок для другого пользователя.

К сожалению, я обновил вопрос ... yes, folder_id - столбец с идентификатором (но folder_id может быть привязан для определенного идентификатора пользователя). Учитывая, что я не знаю, сколько дочерней папки может существовать. Folder_Names уникальны для пользователя, а структуры папок не одинаковы для всех пользователей. Предположим, что user3 нуждается в той же структуре папок user1, и user4 нуждается в такой же структуре папок user2. , и мне будет предоставлен только исходный идентификатор пользователя и конечный пользовательский идентификатор (предполагается, что идентификатор конечного пользователя не имеет структуры папок).

Как я могу это достичь?

+1

Является ли ваш '' Folder_ID' в колонку IDENTITY'? –

+0

Да, это столбец идентичности. – user3398663

+0

Вы хотите сказать, что пользователь1 может иметь родительскую папку как папку user2? –

ответ

2

Вы можете сделать следующее:

SET IDENTITY_INSERT dbo.Folder ON 
go 

declare @maxFolderID int 
select @maxFolderID = max(Folder_ID) from Folder 

insert into Folder 
select @maxFolderID + FolderID, @maxFolderID + Parent_Folder_ID, Folder_Name, 2 
from Folder 
where User_ID = 1 

SET IDENTITY_INSERT dbo.Folder OFF 
go 

EDIT:

SET IDENTITY_INSERT dbo.Folder ON 
GO 

; 
WITH m AS (SELECT MAX(Folder_ID) AS mid FROM  Folder), 
     r AS (SELECT * , 
         ROW_NUMBER() OVER (ORDER BY Folder_ID) + m.mid AS rn 
       FROM  Folder 
         CROSS JOIN m 
       WHERE User_ID = 1 
      ) 
    INSERT INTO Folder 
      SELECT r1.rn , 
        r2.rn , 
        r1.Folder_Name , 
        2 
      FROM r r1 
        LEFT JOIN r r2 ON r2.Folder_ID = r1.Parent_Folder_ID 


SET IDENTITY_INSERT dbo.Folder OFF 
GO 
+0

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

+0

Всегда можно добавить какое-то значение в значения, чтобы гарантировать, что дубликатов не будет. –

+0

@Giorgi: Спасибо за ваш ответ. Я обновил вопрос. Вы не могли бы проверить. – user3398663

1

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

Я использую MERGE ниже, так что я могу получить доступ как источник и inserted таблицы в предложении OUTPUT, которое не позволило INSERT заявления:

declare @FromUserID int 
declare @ToUserID int 
declare @ToCopy table (OldParentID int,NewParentID int) 
declare @ToCopy2 table (OldParentID int,NewParentID int) 

select @FromUserID = 1,@ToUserID = 2 

merge into T1 t 
using (select Folder_ID,Parent_Folder_ID,Folder_Name 
     from T1 where User_ID = @FromUserID and Parent_Folder_ID is null) s 
on 1 = 0 
when not matched then insert (Parent_Folder_ID,Folder_Name,User_ID) 
         values (NULL,s.Folder_Name,@ToUserID) 
output s.Folder_ID,inserted.Folder_ID into @ToCopy (OldParentID,NewParentID); 

while exists (select * from @ToCopy) 
begin 
    merge into T1 t 
    using (select Folder_ID,p2.NewParentID,Folder_Name from T1 
      inner join @ToCopy p2 on p2.OldParentID = T1.Parent_Folder_ID) s 
    on 1 = 0 
    when not matched then insert (Parent_Folder_ID,Folder_Name,User_ID) 
          values (NewParentID,Folder_Name,@ToUserID) 
    output s.Folder_ID,inserted.Folder_ID into @ToCopy2 (OldParentID,NewParentID); 

    --This would be much simpler if you could assign table variables, 
    -- @ToCopy = @ToCopy2 
    -- @ToCopy2 = null 
    delete from @ToCopy; 
    insert into @ToCopy(OldParentID,NewParentID) 
     select OldParentID,NewParentID from @ToCopy2; 
    delete from @ToCopy2; 
end 

(я также написал это на предположение о том, что мы никогда не хотим иметь строки в таблице с неправильными или отсутствующими родительскими ценностями)


в случае логик не ясен - мы сначала находим строки для старого пользователя, у которых нет родителей - те e мы можем четко скопировать для нового пользователя немедленно. На основе этой вставки мы отслеживаем, какие новые значения идентичности были присвоены, с которым старое значение идентичности.

Затем мы продолжаем использовать эту информацию для идентификации следующего набора строк для копирования (в @ToCopy) - поскольку строки, родители которых были просто скопированы, являются следующим набором, подлежащим копированию. Мы зацикливаемся до тех пор, пока не создадим пустой набор, а это означает, что все строки были скопированы.

Это не соответствует родительским/дочерним циклам, но, надеюсь, у вас их нет.

0

Предполагая, что Folder.Folder_ID - это столбец идентификации, вам было бы лучше сделать это в два этапа, первым шагом будет вставить нужные вам папки, а следующий - обновить идентификатор родительской папки.

DECLARE @ExistingUserID INT = 1, 
     @NewUserID INT = 2; 

BEGIN TRAN; 

-- INSERT REQUIRED FOLDERS 
INSERT Folder (Folder_Name, User_ID) 
SELECT Folder_Name, User_ID = @NewUserID 
FROM Folder 
WHERE User_ID = @ExistingUserID; 

-- UPDATE PARENT FOLDER 
UPDATE f1 
SET  Parent_Folder_ID = p2.Folder_ID 
FROM Folder AS f1 
     INNER JOIN Folder AS f2 
      ON f2.Folder_Name = f1.Folder_Name 
      AND f2.user_id = @ExistingUserID 
     INNER JOIN Folder AS p1 
      ON p1.Folder_ID = f2.Parent_Folder_ID 
     INNER JOIN Folder AS p2 
      ON p2.Folder_Name = p1.Folder_Name 
      AND p2.user_id = @NewUserID 
WHERE f1.user_id = @NewUserID; 

COMMIT TRAN; 

Раствор 2

DECLARE @Output TABLE (OldFolderID INT, NewFolderID INT, OldParentID INT); 

DECLARE @ExistingUserID INT = 1, 
     @NewUserID INT = 2; 

BEGIN TRAN; 

MERGE Folder AS t 
USING 
( SELECT * 
    FROM Folder 
    WHERE user_ID = @ExistingUserID 
) AS s 
    ON 1 = 0 -- WILL NEVER BE TRUE SO ALWAYS GOES TO MATCHED CLAUSE 
WHEN NOT MATCHED THEN 
    INSERT (Folder_Name, User_ID) 
    VALUES (s.Folder_Name, @NewUserID) 
OUTPUT s.Folder_ID, inserted.Folder_ID, s.Parent_Folder_ID 
    INTO @Output (OldFolderID, NewFolderID, OldParentID); 

-- UPDATE PARENT FOLDER 
UPDATE f 
SET  Parent_Folder_ID = p.NewFolderID 
FROM Folder AS f 
     INNER JOIN @Output AS o 
      ON o.NewFolderID = f.Folder_ID 
     INNER JOIN @Output AS p 
      ON p.OldFolderID = o.OldParentID; 

COMMIT TRAN; 
+0

Я пытаюсь добавить, где говорится «Решение2» - * Если имена ваших папок не уникальны для user_id, тогда это становится немного сложнее, поскольку вы не можете полагаться на имена папок на 'JOIN', чтобы получить parent_folder_id. Если вы используете 'MERGE', а не вставляете, вы можете вставлять поля, которые вставляются, и те, которые не входят в предложение вывода, что даст вам сопоставление между старым и новым folder_id: * - Но stackoverflow не позволит мне отредактировать этот ответ, и сообщение об ошибке не помогает мне. – GarethD

+0

Сэр, не повезло, он не работает должным образом, если идентификаторы папки сведены для определенного идентификатора пользователя. Да, Folder_name уникально для пользователя. и подумайте, что я не знаю, сколько дочерней папки может существовать. – user3398663