INSERT OUTPUT INTO [duplicate] - sql-server

I've made some modifications to my database and I need to migrate the old data to the new tables. For that, I need to fill a table (ReportOptions) taking the data from the original table (Practice), and fill a second intermediate table (PracticeReportOption).
ReportOption (
ReportOptionId int PK,
field1, field2...
)
Practice (
PracticeId int PK,
field1, field2...
)
PracticeReportOption (
PracticeReportOptionId int PK,
PracticeId int FK,
ReportOptionId int FK,
field1, field2...
)
I made a query to get all the data I need to move from Practice to ReportOptions, but I'm having trouble filling the intermediate table.
--Auxiliary tables
DECLARE #ReportOption TABLE (
PracticeId int, -- This field is not on the actual ReportOption table
field1, field2...
)
DECLARE #PracticeReportOption TABLE (
PracticeId int,
ReportOptionId int,
field1, field2
)
--First I get all the data I need to move
INSERT INTO #ReportOption
SELECT P.practiceId, field1, field2...
FROM Practice P
--I insert it into the new table,
--but somehow I need to have the repation PracticeId / ReportOptionId
INSERT INTO ReportOption (field1, field2...)
OUTPUT #ReportOption.PracticeId, --> this is the field I don't know how to get
inserted.ReportOptionId
INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
FROM #ReportOption
-- This would insert the relationship, If I knew how to get it!
INSERT INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT PracticeId, ReportOptionId
FROM #ReportOption
If I could reference a field that is not from the destination table in the OUTPUT clause, that would be great (I think I can't, but I don't know for sure). Any ideas on how to accomplish my need?

You can do this by using MERGE instead of INSERT.
So replace this:
INSERT INTO ReportOption (field1, field2...)
OUTPUT #ReportOption.PracticeId, --> this is the field I don't know how to get
inserted.ReportOptionId
INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
FROM #ReportOption
with:
MERGE INTO ReportOption USING #ReportOption AS temp ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (field1, field2)
VALUES (temp.Field1, temp.Field2)
OUTPUT temp.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
INTO #PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);
The key is to use a predicate that will never be true (1 = 0) in the merge search condition, so you will always perform the insert, but have access to fields in both the source and destination tables.
Here is the entire code I used to test it:
CREATE TABLE ReportOption (
ReportOptionID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
CREATE TABLE Practice (
PracticeID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
CREATE TABLE PracticeReportOption (
PracticeReportOptionID INT IDENTITY(1, 1),
PracticeID INT,
ReportOptionID INT,
Field1 INT,
Field2 INT
)
INSERT INTO Practice VALUES (1, 1), (2, 2), (3, 3), (4, 4)
MERGE INTO ReportOption r USING Practice p ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (field1, field2)
VALUES (p.Field1, p.Field2)
OUTPUT p.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
INTO PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);
SELECT *
FROM PracticeReportOption
DROP TABLE ReportOption
DROP TABLE Practice
DROP TABLE PracticeReportOption
More reading, and the source of all that I know on the subject is here.

Maybe someone who uses MS SQL Server 2005 or lower will find this answer useful.
MERGE will only work for SQL Server 2008 or higher.
For the rest, I found another workaround which will give you the ability to create kind of mapping tables.
Here's how Resolution will look like for SQL 2005:
DECLARE #ReportOption TABLE (
ReportOptionID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
DECLARE #Practice TABLE(
PracticeID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
DECLARE #PracticeReportOption TABLE(
PracticeReportOptionID INT IDENTITY(1, 1),
PracticeID INT,
ReportOptionID INT,
Field1 INT,
Field2 INT
)
INSERT INTO #Practice (Field1, Field2) VALUES (1, 1)
INSERT INTO #Practice (Field1, Field2) VALUES (2, 2)
INSERT INTO #Practice (Field1, Field2) VALUES (3, 3)
INSERT INTO #Practice (Field1, Field2) VALUES (4, 4)
INSERT INTO #ReportOption (field1, field2)
OUTPUT INSERTED.ReportOptionID, INSERTED.Field1, INSERTED.Field2
INTO #PracticeReportOption (ReportOptionID, Field1, Field2)
SELECT Field1, Field2
FROM #Practice
ORDER BY PracticeID ASC;
WITH CTE AS (
SELECT PracticeID,
ROW_NUMBER() OVER ( ORDER BY PracticeID ASC ) AS ROW
FROM #Practice
)
UPDATE M
SET M.PracticeID = S.PracticeID
FROM #PracticeReportOption AS M
JOIN CTE AS S ON S.ROW = M.PracticeReportOptionID
SELECT * FROM #PracticeReportOption
The main trick is that we are filling the mapping table twice with ordered data from the source and destination table.
For more details, see Merging Inserted Data Using OUTPUT in SQL Server 2005.

Related

OUTPUT INSERTED FROM Source Table Column [duplicate]

I've made some modifications to my database and I need to migrate the old data to the new tables. For that, I need to fill a table (ReportOptions) taking the data from the original table (Practice), and fill a second intermediate table (PracticeReportOption).
ReportOption (
ReportOptionId int PK,
field1, field2...
)
Practice (
PracticeId int PK,
field1, field2...
)
PracticeReportOption (
PracticeReportOptionId int PK,
PracticeId int FK,
ReportOptionId int FK,
field1, field2...
)
I made a query to get all the data I need to move from Practice to ReportOptions, but I'm having trouble filling the intermediate table.
--Auxiliary tables
DECLARE #ReportOption TABLE (
PracticeId int, -- This field is not on the actual ReportOption table
field1, field2...
)
DECLARE #PracticeReportOption TABLE (
PracticeId int,
ReportOptionId int,
field1, field2
)
--First I get all the data I need to move
INSERT INTO #ReportOption
SELECT P.practiceId, field1, field2...
FROM Practice P
--I insert it into the new table,
--but somehow I need to have the repation PracticeId / ReportOptionId
INSERT INTO ReportOption (field1, field2...)
OUTPUT #ReportOption.PracticeId, --> this is the field I don't know how to get
inserted.ReportOptionId
INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
FROM #ReportOption
-- This would insert the relationship, If I knew how to get it!
INSERT INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT PracticeId, ReportOptionId
FROM #ReportOption
If I could reference a field that is not from the destination table in the OUTPUT clause, that would be great (I think I can't, but I don't know for sure). Any ideas on how to accomplish my need?
You can do this by using MERGE instead of INSERT.
So replace this:
INSERT INTO ReportOption (field1, field2...)
OUTPUT #ReportOption.PracticeId, --> this is the field I don't know how to get
inserted.ReportOptionId
INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
FROM #ReportOption
with:
MERGE INTO ReportOption USING #ReportOption AS temp ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (field1, field2)
VALUES (temp.Field1, temp.Field2)
OUTPUT temp.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
INTO #PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);
The key is to use a predicate that will never be true (1 = 0) in the merge search condition, so you will always perform the insert, but have access to fields in both the source and destination tables.
Here is the entire code I used to test it:
CREATE TABLE ReportOption (
ReportOptionID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
CREATE TABLE Practice (
PracticeID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
CREATE TABLE PracticeReportOption (
PracticeReportOptionID INT IDENTITY(1, 1),
PracticeID INT,
ReportOptionID INT,
Field1 INT,
Field2 INT
)
INSERT INTO Practice VALUES (1, 1), (2, 2), (3, 3), (4, 4)
MERGE INTO ReportOption r USING Practice p ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (field1, field2)
VALUES (p.Field1, p.Field2)
OUTPUT p.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
INTO PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);
SELECT *
FROM PracticeReportOption
DROP TABLE ReportOption
DROP TABLE Practice
DROP TABLE PracticeReportOption
More reading, and the source of all that I know on the subject is here.
Maybe someone who uses MS SQL Server 2005 or lower will find this answer useful.
MERGE will only work for SQL Server 2008 or higher.
For the rest, I found another workaround which will give you the ability to create kind of mapping tables.
Here's how Resolution will look like for SQL 2005:
DECLARE #ReportOption TABLE (
ReportOptionID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
DECLARE #Practice TABLE(
PracticeID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
DECLARE #PracticeReportOption TABLE(
PracticeReportOptionID INT IDENTITY(1, 1),
PracticeID INT,
ReportOptionID INT,
Field1 INT,
Field2 INT
)
INSERT INTO #Practice (Field1, Field2) VALUES (1, 1)
INSERT INTO #Practice (Field1, Field2) VALUES (2, 2)
INSERT INTO #Practice (Field1, Field2) VALUES (3, 3)
INSERT INTO #Practice (Field1, Field2) VALUES (4, 4)
INSERT INTO #ReportOption (field1, field2)
OUTPUT INSERTED.ReportOptionID, INSERTED.Field1, INSERTED.Field2
INTO #PracticeReportOption (ReportOptionID, Field1, Field2)
SELECT Field1, Field2
FROM #Practice
ORDER BY PracticeID ASC;
WITH CTE AS (
SELECT PracticeID,
ROW_NUMBER() OVER ( ORDER BY PracticeID ASC ) AS ROW
FROM #Practice
)
UPDATE M
SET M.PracticeID = S.PracticeID
FROM #PracticeReportOption AS M
JOIN CTE AS S ON S.ROW = M.PracticeReportOptionID
SELECT * FROM #PracticeReportOption
The main trick is that we are filling the mapping table twice with ordered data from the source and destination table.
For more details, see Merging Inserted Data Using OUTPUT in SQL Server 2005.

How to combine two FOR XML AUTO into 1 XML?

We are using SQL Server 2012.
Table myTbl has a one to many relationship to table myAllocation
Table ABC_myTbl has a one to many relationship to table ABC_myAllocation
The below query combined 2 FOR XML AUTO into 1 XML, but the problem is ID, SystemSource, Manager are included in element TradeTicket instead of on their own, and accountManager, unitPrice are included in element allocationRow instead of on their own.
Thank you
SELECT '<?xml version="1.0"?>'+
(SELECT
( SELECT trTicket.[id],trTicket.[manager],'PFM' as SystemSource
,allocationRow.accountNumber,allocationRow.unitPrice
FROM myTbl AS trTicket
LEFT JOIN myAllocation AS allocationRow ON allocationRow.trade_ticket_id=trTicket.id
WHERE trTicket.ID = 8779631
ORDER BY trTicket.id,allocationRow.AccountNumber
FOR XML AUTO, type)
,
(
SELECT trTicket.[id],trTicket.[manager],'CRD' as SystemSource
,allocationRow.accountNumber,allocationRow.unitPrice
FROM ABC_myTbl AS trTicket
LEFT JOIN ABC_myAllocation AS allocationRow ON allocationRow.trade_ticket_id=trTicket.id
WHERE trTicket.ID = 8
ORDER BY trTicket.id,allocationRow.AccountNumber
FOR XML AUTO, type)
FOR XML PATH('trTickets'), ELEMENTS) AS XMLResult
This is the current result:
<?xml version="1.0"?>
<trTickets>
<trTicket id="8779631" SystemSource="PFM" manager="MCM">
<allocationRow accountNumber="292 " unit_Price="300"/>
</trTicket>
<trTicket id="8" SystemSource="CRD" manager="DOYLE">
<allocationRow unitPrice="100" accountNumber="F11 "/>
<allocationRow unitPrice="200" accountNumber="F22 "/>
</trTicket>
</trTickets>
This is the desired result that I am looking for:
<?xml version="1.0"?>
<trTickets>
<trTicket>
<id>8</id>
<manager>DOYLE</manager>
<SystemSource>CRD</SystemSource>
<allocationRow>
<accountNumber>F11</accountNumber>
<unitPrice>100</unitPrice>
</allocationRow>
<allocationRow>
<accountNumber>F22</accountNumber>
<unitPrice>200</unitPrice>
</allocationRow>
</trTicket>
<trTicket>
<id>8779631</id>
<manager>MCM</manager>
<SystemSource>PFM</SystemSource>
<allocationRow>
<accountNumber>292</accountNumber>
<unitPrice>300</unitPrice>
</allocationRow>
</trTicket>
</trTickets>
Data sample:
Table ABC_myTbl:
ID Manager
-----------
8 DOYLE
Table ABC_myAllocation:
accountNumber unitPrice
-------------------------
F11 100
F22 200
Table myTbl:
ID Manager
---------------
8779631 MCM
Table myAllocation:
accountNumber unitPrice
--------------------------
292 300
DDL for the tables and their data:
CREATE TABLE dbo.ABC_myTbl
(
ID INT NOT NULL,
MANAGER VARCHAR(10) NOT NULL
)
CREATE TABLE dbo.myTbl
(
ID INT NOT NULL,
MANAGER VARCHAR(10) NOT NULL
)
CREATE TABLE dbo.ABC_myAllocation
(
accountNumber VARCHAR(10) NOT NULL,
unitprice NUMERIC(10, 3) NOT NULL
)
CREATE TABLE dbo.myAllocation
(
accountNumber VARCHAR(10) NOT NULL,
unitprice NUMERIC(10, 3) NOT NULL
)
INSERT INTO dbo.ABC_myTbl VALUES (8,'DOYLE')
INSERT INTO dbo.ABC_myAllocation VALUES ('F11',100)
INSERT INTO dbo.ABC_myAllocation VALUES ('F22',200)
INSERT INTO dbo.myTbl VALUES (8779631,'MCM')
INSERT INTO dbo.myAllocation VALUES ('292',300)
I didn't wait for your DDL and sample data population. So I created a conceptual sample for you. Please pay attention that the tables have implied relationships and they are used in the WHERE clauses.
SQL
-- DDL and sample data population, start
DECLARE #tbl1 TABLE (ID INT PRIMARY KEY, Manager VARCHAR(20));
INSERT INTO #tbl1 (ID, Manager) VALUES
(8, 'DOYLE'),
(9, 'XYZ');
DECLARE #tbl1Child TABLE (accountNumber CHAR(3) PRIMARY KEY, ParentID INT, unitPrice DECIMAL(10,2));
INSERT INTO #tbl1Child (accountNumber, ParentID, unitPrice) VALUES
('F11', 8, 100)
,('F22', 8, 200)
,('F70', 9, 770);
DECLARE #tbl2 TABLE (ID INT PRIMARY KEY, Manager VARCHAR(20));
INSERT INTO #tbl2 (ID, Manager) VALUES
(8779631, 'MCM')
,(8779555, 'TTT');
DECLARE #tbl2Child TABLE (accountNumber CHAR(3) PRIMARY KEY, ParentID INT, unitPrice DECIMAL(10,2));
INSERT INTO #tbl2Child (accountNumber, ParentID, unitPrice) VALUES
('292', 8779631, 300)
,('255', 8779555, 500);
-- DDL and sample data population, end
SELECT TOP(1) NULL
, (
SELECT *
, (
SELECT * FROM #tbl1Child AS c
WHERE p.ID = c.ParentID
FOR XML PATH('allocation_row'), TYPE
)
FROM #tbl1 AS p
FOR XML PATH('tradeTicket'), TYPE
)
, (
SELECT *
, (
SELECT * FROM #tbl2Child AS c
WHERE p.ID = c.ParentID
FOR XML PATH('allocation_row'), TYPE
)
FROM #tbl2 AS p
FOR XML PATH('tradeTicket'), TYPE
)
FROM #tbl1
FOR XML PATH(''), TYPE, ROOT('tradeTickets');
Output
<tradeTickets>
<tradeTicket>
<ID>8</ID>
<Manager>DOYLE</Manager>
<allocation_row>
<accountNumber>F11</accountNumber>
<ParentID>8</ParentID>
<unitPrice>100.00</unitPrice>
</allocation_row>
<allocation_row>
<accountNumber>F22</accountNumber>
<ParentID>8</ParentID>
<unitPrice>200.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>9</ID>
<Manager>XYZ</Manager>
<allocation_row>
<accountNumber>F70</accountNumber>
<ParentID>9</ParentID>
<unitPrice>770.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>8779555</ID>
<Manager>TTT</Manager>
<allocation_row>
<accountNumber>255</accountNumber>
<ParentID>8779555</ParentID>
<unitPrice>500.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>8779631</ID>
<Manager>MCM</Manager>
<allocation_row>
<accountNumber>292</accountNumber>
<ParentID>8779631</ParentID>
<unitPrice>300.00</unitPrice>
</allocation_row>
</tradeTicket>
</tradeTickets>

How to autoincrement the id without identity?

I'm trying do to a bulk insert from another table in sql server. My query is currently like that :
INSERT INTO Table1(Id, Value)
SELECT ??, Value
FROM Table2;
Now, my problem is obviously by what I replace ??. Id is an integer column without an identity property. I would like that for each inserted row, Id take the current max(Id) + 1.
Can I do that directly in my insert command?
If you were using a newer version of SQL Server (2008+) you could try ROW_NUMBER():
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + ROW_NUMBER() OVER (ORDER BY Value) ID,
Value
FROM Table2;
SQL Fiddle
Since you are using SQL Server 2000, you could try like bellow:
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.Value <= a.Value),
a.Value
FROM Table2 a
But it will only works if Value in Table2 is unique
SQL Fiddle
If Table2 has a primary key (field PK), then you could use:
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.PK <= a.PK),
a.Value
FROM Table2 a
Here is one wicked way.
We create a temp table with identity to generate new ids. This way we avoid the while loop.
DECLARE #CurrentMaxID INT,
#DynamicQuery NVARCHAR(MAX)
--TODO : Acquired table lock here on table1
SELECT #FirstNextID = ISNULL(MAX(Id), 0)
FROM Table1 --WITH(TABLOCK)
CREATE TABLE #TempTableWithID( Table2Id INT,
Table1FuturId INT IDENTITY(1, 1))
INSERT INTO #TempTableWithID(Table2Id)
SELECT Id --Here we use identity to generate table1 futur id
FROM Table2
INSERT INTO Table1(Id, value)
SELECT Temp.Table1FuturId + #FirstNextID,
Table2.Value
FROM Table2
INNER JOIN #TempTableWithID AS Temp ON Table2.Id = Temp.Table2Id
--TODO : release table lock here on table1
DROP TABLE #TempTableWithID
If I'm understanding you correctly, this should work.
CREATE TABLE #tbl1 (ID int, Value float)
CREATE TABLE #tbl2 (ID int, Value float)
INSERT INTO #tbl2 values (4, 2.0)
INSERT INTO #tbl2 values (8, 3.0)
INSERT INTO #tbl2 values (6, 4.0)
INSERT INTO #tbl1 values (1,1.0)
INSERT INTO #tbl1 values (3,3)
INSERT INTO #tbl1 values (9,3)
/*meat and potatoes start*/
INSERT INTO #tbl1(Id, Value)
SELECT (SELECT MAX(ID) FROM #tbl1) + ROW_NUMBER() OVER (ORDER BY Value) ID, Value
FROM #tbl2;
/*meat and potatoes end*/
Select * From #tbl1
drop table #tbl1
drop table #tbl2
Why not IDENT_CURRENT() ?
SELECT IDENT_CURRENT('yourtablename')
It gives you the next ID reference. But this only works if the ID column has IDENTITY turned on.
OR you can try a SEQUENCE and the NEXT VALUE FOR.
i.e.
CREATE TABLE Test.TestTable
(CounterColumn int PRIMARY KEY,
Name nvarchar(25) NOT NULL) ;
GO
INSERT Test.TestTable (CounterColumn,Name)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Syed') ;
GO
SELECT * FROM Test.TestTable;
GO

Converting Temporary table code to CTE to improve performance

I have the following code in my Stored procedure:
CREATE MyProc
AS
CREATE TABLE #Table1
(
Field1 INT NOT NULL,
Field2 VARCHAR NULL,
Field3 VARCHAR NULL,
Field4 VARCHAR NULL
);
-----Populate Temporary Table-----
INSERT INTO #Table1 (Field1)
SELECT val1 FROM Tab1
INSERT INTO #Table1 (Field1,Field2)
SELECT val1,"val2" FROM Tab2
INSERT INTO #Table1 (Field1,Field2,Field3)
SELECT val1,"val2","val3" FROM Tab3
INSERT INTO #Table1 (Field1,Field2,Field3,Field4)
SELECT val1,"val2","val3","val4" FROM Tab3
SELECT Field1,Field2,Field3,Field4 FROM #Table1
DROP #Table1
There are around 10-15 INSERT statements which are populating the temporary table.I want to tune the stored proc to improve performance using CTE or some other way.How I can rewrite the procedure?
i'm not entirely sure this is going to be better in any way but this is using a cte to get the same output (i used some lazy-ass value arrays to get it to work out of the box, you can ofcourse substitute those with actual tables):
with cte (f1, f2, f3, f4)
as (
select 1, null, null, null
from (
values (1)
) t1(n1)
union all
select 1, 2, null, null
from (
values (1, 2)
) t1(n1, n2)
union all
select 1, 2, 3, null
from (
values (1, 2, 3)
) t1(n1, n2, n3)
union all
select 1, 2, 3, 4
from (
values (1, 2, 3, 4)
) t1(n1, n2, n3, n4)
)
select *
from cte

T-SQL: Using INSERT INTO & OUTPUT inside CASE clause

I need to assign a new_id to a field inside a CASE clause.
Here is the example:
INSERT INTO [table1]
(field1, field2, field3, field4, field5, field6)
SELECT #someID1, field1_from_table2, #someID2, field2_from_table2,
CASE
WHEN field3_from_table2 IS NULL
THEN INSERT INTO [table3]
OUTPUT inserted.some_new_id
DEFAULT VALUES
ELSE field3_from_table2
END
FROM [table2]
The code fragment
INSERT INTO [table3]
OUTPUT inserted.some_new_id
DEFAULT VALUES
works fine when used outside of the CASE clause.
The main problem is that I have to assign a new_id obtained from table3 when the field3_from_table2 I'm trying to insert into table1 IS NULL.
table3 is just an ID dispatcher.
Any idea or workaround to this problem?
Thanks in advance.
As pointed by marc_s, you can't do that using the a case.
You can see the correct case syntax here.
To solve your problem the best solution is to use a cursor.
You can see an code example for a sp with a cursor here.
Assuming table3 generates sequential integer values, like a IDENTITY property with increments of value 1, you can generate those yourself using ROW_NUMBER(), and then INSERTing the generated values into table3.
But, since you're generating the values yourself, you must lock table3 to prevent changes ocurring to it while the statement runs.
Let's set up a test case.
USE tempdb
GO
IF OBJECT_ID('table1', 'U') IS NOT NULL DROP TABLE table1;
IF OBJECT_ID('table2', 'U') IS NOT NULL DROP TABLE table2;
IF OBJECT_ID('table3', 'U') IS NOT NULL DROP TABLE table3;
CREATE TABLE table1 (
field1 int,
field2 int,
field3 int,
field4 int,
field5 int
);
CREATE TABLE table2 (
field1_from_table2 int,
field2_from_table2 int
);
CREATE TABLE table3 (
field1_from_table3 int IDENTITY(1,1)
);
INSERT INTO table2
VALUES (1000, 2000)
, (1001, NULL);
GO
-- INSERT 20 records to generate some IDENTITY increments.
INSERT INTO table3 DEFAULT VALUES
GO 20
Here's the example code to generate the sequential values.
BEGIN TRANSACTION;
SET IDENTITY_INSERT table3 ON;
GO
DECLARE #someID1 int = 100
, #someID2 int = 200;
DECLARE #output table (field4 int, field5 int);
-- Lock table3 exclusively to prevent INSERTs that spoil IDENTITY values.
SELECT TOP(0) 1 FROM table3 WITH (HOLDLOCK, TABLOCKX);
-- INSERT into table1, generating sequential integers
-- and saving the output in #output.
INSERT INTO table1
OUTPUT inserted.field4
, inserted.field5
INTO #output(field4, field5)
SELECT #someID1
, field1_from_table2
, #someID2
, field2_from_table2
, CASE
WHEN field2_from_table2 IS NOT NULL THEN field2_from_table2
ELSE (ROW_NUMBER() OVER (PARTITION BY field2_from_table2
ORDER BY field2_from_table2))
+ (SELECT MAX(field1_from_table3) FROM table3)
END
FROM table2;
-- INSERT generated integer values.
INSERT INTO table3 (field1_from_table3)
SELECT field5
FROM #output
WHERE field4 IS NULL;
SET IDENTITY_INSERT table3 OFF;
GO
COMMIT;
SELECT * FROM table1;
SELECT * FROM table2;
SELECT * FROM table3;

Resources