I have two tables, TableA
and TableB
like this:
TableA
-------------------------------------------
| id | some_data | new_FK_column_on_B |
| ---- | ----------- | ------------------ |
| 1 | ... | null |
| ... | ... | null |
| 999 | ... | null |
-------------------------------------------
TableB
----------------------------
| id | some_other_data |
| ---- | ----------------- |
| | |
----------------------------
At the moment, TableB
is empty, and FK column in TableA
is null
for all rows. I need to write one-time initializing scrit to populate TableB
and initialize FK column for some rows (criterial, not for all) in TableA
by identifiers from rows, inserted in TableB
.
I know two ways to do this:
1) using while
and scope_identity()
, inserting new row into TableB
and updating TableA
on each iteration, while exists rows in TableA
, which should be updated
while (exists (select 1 from TableA where [condition]))
begin
insert into TableB (some_other_data) values ('some_other_data')
update TableA set new_FK_column_on_B
where id = (select top 1 id from TableA where [condition])
end
2) create temp column in TableB
, storing id
of row in TableA
, for which it was inserted, and then update TableA
using join
alter table TableB add temp int
go
insert into TableB (some_other_data, temp) select 'some_other_data', id from TableA where [condition]
update TableA
set new_FK_column_on_B = b.id
from TableB as b
join TableA as a on a.id = b.temp
alter table TableB drop column temp
Also I was trying to use somehow output
from insert
like this, but it's syntax is incorrect:
update TableA
set new_FK_column_on_B =
(
select insertedId from
(
insert into TableB (some_other_data)
output inserter.id as insertedId
values ('some_other_data')
)
)
where [condition]
Is there any easier way to do this whithout using while
or modifing any table?
I found this question when searching for a solution a similar case. The only difference was that I wanted to fill TableB with an row for each row in TableA (no where-clause).
I found a solution to the third option you suggeted (using output from insert), however, since you are using INSERT with data from a SELECT, you cannot use the OUTPUT clause. This pointed me in the right direction:
DECLARE @MyTableVar table(tableBId int, tableAId int)
MERGE INTO TableB
using TableA AS AP
on 1=0
WHEN NOT MATCHED THEN
Insert(some_other_data)
Values('some_other_data')
Output inserted.ID, AP.ID INTO @MyTableVar;
update TableA set new_FK_column_on_B = (select tableBId from @MyTableVar where tableAId = TableA.ID)
Be aware that executing this a seconds time will create new entries in TableB. If you only want to create new rows in TableB, where there is no foreign key set in TableA, you can use this script:
DECLARE @MyTableVar TABLE(tableBId int, tableAId int)
MERGE INTO TableB AS B
USING TableA AS AP
ON A.new_FK_column_on_B = B.id
WHEN NOT MATCHED THEN
INSERT(some_data)
VALUES(AP.some_data)
OUTPUT inserted.ID, AP.ID INTO @MyTableVar;
UPDATE TableA SET new_FK_column_on_B = (
SELECT tableBId
FROM @MyTableVar
WHERE tableAId = TableA.ID )
WHERE TableA.new_FK_column_on_B IS NULL;
You can do all this as set operations:
insert into b(some_data)
select distinct some_data
from a;
update a
set new_FK_column_on_B = b.id
from a join
b
on a.some_data = b.some_data;
This assumes that the id
column in b
is declared as identity()
, so it gets assigned automatically. This is a good idea, but if you want to do this manually, then the first query would be something like:
insert into b(some_data)
select row_number() over (order by (select null)), some_data
from (select distinct some_data
from a
) a;
There is no need for a while
loop.
DISCLAIMER: This is not tested at all, I wrote this in notepad:
DECLARE @TableAValues TABLE (IdTableA int, SomeData varchar)
INSERT INTO @TableAValues(IdTableA, SomeData)
SELECT id, 'some_data' FROM TableA
DECLARE @TableBIds TABLE (IdTableB int)
INSERT INTO TableB(SomeData)
OUTPUT INSERTED.ID INTO @TableBIds
SELECT SomeData FROM @TableAValues
UPDATE ta
SET ta.new_FK_column_on_B = tbi.IdTableB
FROM dbo.TableA AS ta
INNER JOIN @TableAValues AS tav ON ta.id = tav.IdTableA -- used in case more records were added to table in the interim.
LEFT OUTER JOIN @TableBIds tbi On tav.RowNum = tbi.RowNum
Note: I am using In memory tables, but if you were concerned about memory usage, you could probably just switch those out for temp tables on disk.
The idea I was going for here:
I'd be very surprised if my code sample above works as is, but hopefully the idea will be useful, if not my execution.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.