Как скопировать таблицы, избегая курсоров в SQL? - PullRequest
9 голосов
/ 30 мая 2011

Я хочу написать скрипт на SQL, который будет копировать эти 2 таблицы (A, B) в другие 2 таблицы (C, D) с той же структурой, что и A, B соответственно.

ВАЖНО:

  1. Таблицы C, D NOT необходимо пустое
  2. Несколько процессов могут одновременно вызывать скрипт

Таблица Aимеет внешний ключ (fk_a_b) таблицы B

   ________________________  _________________
   |        Table A       |  |   Table B     |  
   |______________________|  |_______________|
   | id     FK_A_B   name |  | id    visible |
   | ----- -------- ------|  | ----- --------|
   | 1      21       n1   |  | 21     true   |
   | 5      32       n2   |  | 32     false  |
   ------------------------  -----------------

Допустим, что после копирования таблицы B в D это то, что я получаю

   ________________
   |   Table D    |  
   |______________|
   | id   visible |
   | ----- -------|
   | 51    true   |
   | 52    false  |
   ----------------

Теперь, когда я скопирую таблицу ACI нужно как-то знать, что ID = 21 теперь отображается на ID = 51, а ID = 32 на ID = 52.Наконец, таблица C будет иметь вид:

   ________________________
   |        Table C       |
   |______________________|
   | id     FK_C_D   name |
   | ----- -------- ------|
   | 61      51       n1  |
   | 62      52       n2  |
   ------------------------

Поскольку несколько процессов могут вызывать скрипт одновременно, я НЕ МОГУ изменить таблицу A, B, чтобы добавить несколько вспомогательных столбцов.Итак, для достижения этого я использовал CURSOR.Я скопировал строку за строкой таблицы B и управляемую временную таблицу для сопоставления OldId с NewId (21-> 51,32-> 52), а затем использовал эту временную таблицу для копирования таблицы A.

Я читал, чтоКУРСОР это плохая практика.Итак, есть ли другой способ сделать это?

Спасибо

Ответы [ 4 ]

7 голосов
/ 30 мая 2011

Вы можете использовать предложение output с оператором merge, чтобы получить соответствие между исходным и целевым идентификаторами.Описано в этом вопросе. Использование merge..output для отображения между source.id и target.id

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

Пример установки данных:

-- @A and @B is the source tables
declare @A as table
(
  id int,
  FK_A_B int,
  name varchar(10)
)

declare @B as table
(
  id int,
  visible bit
)  

-- Sample data in @A and @B
insert into @B values (21, 1),(32, 0)
insert into @A values (1, 21, 'n1'),(5, 32, 'n2')


-- @C and @D is the target tables with id as identity columns
declare @C as table
(
  id int identity,
  FK_C_D int not null,
  name varchar(10)
)

declare @D as table
(
  id int identity,
  visible bit
)  

-- Sample data already in @C and @D
insert into @D values (1),(0)
insert into @C values (1, 'x1'),(1, 'x2'),(2, 'x3')

Копирование данных:

-- The @IdMap is a table that holds the mapping between
-- the @B.id and @D.id (@D.id is an identity column)
declare @IdMap table(TargetID int, SourceID int)

-- Merge from @B to @D.
merge @D as D             -- Target table
using @B as B             -- Source table
on 0=1                    -- 0=1 means that there are no matches for merge
when not matched then
  insert (visible) values(visible)    -- Insert to @D
output inserted.id, B.id into @IdMap; -- Capture the newly created inserted.id and
                                      -- map that to the source (@B.id)

-- Add rows to @C from @A with a join to
-- @IdMap to get the new id for the FK relation
insert into @C(FK_C_D, name)
select I.TargetID, A.name 
from @A as A
  inner join @IdMap as I
    on A.FK_A_B = I.SourceID

Результат:

select *
from @D as D
  inner join @C as C
    on D.id = C.FK_C_D

id          visible id          FK_C_D      name
----------- ------- ----------- ----------- ----------
1           1       1           1           x1
1           1       2           1           x2
2           0       3           2           x3
3           1       4           3           n1
4           0       5           4           n2

Вы можете проверить код здесь: http://data.stackexchange.com/stackoverflow/q/101643/using-merge-to-map-source-id-to-target-id

0 голосов
/ 01 июня 2011

Надеюсь, это поможет

http://pastebin.com/embed_iframe.php?i=fkUd4RPY

0 голосов
/ 30 мая 2011

Вы можете сделать что-то вроде этого:

if object_id('tempdb..#TableB') is not null
    drop table #TableB

select identity(int) RowId, *
into #TableB
from TableB

if object_id('tempdb..#TableDIds') is not null
    drop table #TableDIds
create table  #TableDIds (RowId int identity(1,1), Id int)

insert TableD
output inserted.Id into #TableDIds
select Visible
from #TableB
order by RowId

insert TableC
select tdi.Id, ta.name
from TableA ta
    join #TableB tb on
        ta.FK_A_B = tb.Id
    join #TableDIds tdi on
        tdi.RowId = tb.RowId

Я использовал следующую настройку:

create table TableB
(
    Id int not null primary key,
    Visible bit not null
)

create table TableA
(
    Id int not null, 
    FK_A_B int not null foreign key references TableB(Id), 
    Name varchar(10) not null
)

create table TableD
(
    Id int identity(1,1) primary key,
    Visible bit not null
)

create table TableC
(
    Id int identity(1,1), 
    FK_C_D int not null references TableD(Id), 
    Name varchar(10) not null
)

insert TableB
values
    (21, 1),
    (32, 0)

insert TableA
values
    (1, 21, 'n1'),
    (5, 32, 'n2')
0 голосов
/ 30 мая 2011

Например, SQL Server добавляет поля rowguid в таблицы, которые включены в некоторые публикации для репликации слиянием.Я думаю, такой подход можно использовать в вашей задаче.Идея состоит в том, чтобы добавить пару полей GUID, которые будут играть роль глобальных идентификаторов, чтобы мы могли использовать их в обеих парах таблиц master-datails

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...