UPDATE: the issue does not happen when run against SQL Server 2008. So this is something strange (or wrong) with SQL Server 2000.
I try to do a simple insert on SQL Server 2000:
INSERT INTO UserAddresses (UserId, AddressId)
SELECT UserId, Id
FROM Addresses
and I get this:
INSERT statement conflicted with
COLUMN FOREIGN KEY constraint
'FK569ABB5045EE0940'. The conflict
occurred in database 'Orders', table
'Addresses', column 'Id'.
I'm well aware of what this means, but I can't understand why conflict happens - notice that I insert IDs from the Addresses table, so they DO exist! Why can't SQL Server find them on the foreign key end in the Addresses table? Should I do silly
SELECT * FROM Addresses
WHERE Id NOT IN (SELECT Id FROM Addresses)
or what?
Some more info: the IDs are GUIDs, data comes from the legacy DB (import). First I populate Addresses, then try to insert into UserAddresses. If I do SELECT TOP 100 ... it works... so it's a problem with some record but I can't understand why it happens.
CREATE TABLE [Addresses] (
[Id] [uniqueidentifier] NOT NULL ,
PRIMARY KEY CLUSTERED ([Id]) ON [PRIMARY] ,
) ON [PRIMARY]
CREATE TABLE [Users] (
[Id] [uniqueidentifier] NOT NULL ,
PRIMARY KEY CLUSTERED ([Id]) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [UserAddresses] (
[UserId] [uniqueidentifier] NOT NULL ,
[AddressId] [uniqueidentifier] NOT NULL ,
CONSTRAINT [FK569ABB5045EE0940] FOREIGN KEY
(
[AddressId]
) REFERENCES [Addresses] (
[Id]
),
CONSTRAINT [UserAddressesToAddressFK] FOREIGN KEY
(
[UserId]
) REFERENCES [Users] (
[Id]
)
) ON [PRIMARY]
ALTER TABLE Addresses ADD UserId UNIQUEIDENTIFIER
INSERT INTO Addresses (UserId, Id)
SELECT legacy_userid, legacy_single_useraddressid -- both are guids
FROM LegacyUsers INNER JOIN LegacyAddresses
UPDATE: I've just done this without errors (query batch completed):
DECLARE c CURSOR FOR SELECT UserId, Id FROM Addresses
OPEN c
DECLARE #uid UNIQUEIDENTIFIER, #aid UNIQUEIDENTIFIER
FETCH NEXT FROM c INTO #uid, #aid
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #aid
INSERT INTO UserAddresses (UserId, AddressId)
VALUES (#uid, #aid)
FETCH NEXT FROM c INTO #uid, #aid
END
CLOSE c
DEALLOCATE c
I wonder why INSERT fails while foreach cursor works...
UPDATE: oops, after cursor completed, INSERT works, too. But it never works standalone. Here's what I do:
Run the import script so that it populates Addresses table
Manually run INSERT - it fails
Manually run CURSOR - it works
DELETE FROM UserAddresses
Manually run INSERT - it works now
Is it a magic or I'm a complete idiot missing something?
UPDATE: If I do
ALTER TABLE UserAddresses DROP CONSTRAINT FK569ABB5045EE0940
INSERT INTO UserAddresses (UserId, AddressId)
SELECT UserId, Id
FROM Addresses
alter table UserAddresses
add constraint FK569ABB5045EE0940
foreign key (AddressId)
references Addresses
it also works. I think it's a bug in SQL Server 2000 despite the "never blame the compiler" rule.
Update - The "harry" Schema
gbn commented that this could be a schema issue. I updated my original code example and was able to get (almost*) the exact error.
(* Note that I'm running this on 2008 and the OP is running on 2000. SQL 2008 schema-qualifies the table in the error message.)
Updated Code - The "harry" Schema
SET NOCOUNT ON
GO
--<< ========================== DROPS ==========================
IF OBJECT_ID('tempdb..#UserGUIDs') IS NOT NULL
DROP TABLE #UserGUIDs
GO
IF OBJECT_ID('tempdb..#AddressGUIDs') IS NOT NULL
DROP TABLE #AddressGUIDs
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('UserAddresses'))
DROP TABLE [UserAddresses]
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('Users'))
DROP TABLE [Users]
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('dbo.Addresses'))
DROP TABLE dbo.[Addresses]
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('harry.Addresses'))
DROP TABLE harry.[Addresses]
GO
--<< ========================== TABLES ==========================
--<< Users
CREATE TABLE [Users] (
[Id] uniqueidentifier NOT NULL DEFAULT NEWID() PRIMARY KEY,
[UserName] varchar(10) NOT NULL
) ON [PRIMARY]
GO
--<< Addresses
CREATE TABLE harry.[Addresses] (
[Id] uniqueidentifier NOT NULL DEFAULT NEWID() PRIMARY KEY,
[Address1] varchar(20) NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE dbo.[Addresses] (
[Id] uniqueidentifier NOT NULL DEFAULT NEWID() PRIMARY KEY,
[Address1] varchar(20) NOT NULL
) ON [PRIMARY]
GO
--<< UserAddresses
CREATE TABLE [UserAddresses] (
[UserId] uniqueidentifier NOT NULL,
[AddressId] uniqueidentifier NOT NULL,
CONSTRAINT [FK569ABB5045EE0940] FOREIGN KEY ([AddressId]) REFERENCES [Addresses] ([Id]),
CONSTRAINT [UserAddressesToAddressFK] FOREIGN KEY ([UserId]) REFERENCES [Users] ([Id])
) ON [PRIMARY]
GO
--<< ========================== DATA ==========================
--<< Populate Users
CREATE TABLE #UserGUIDs ([UserId] uniqueidentifier)
GO
INSERT INTO [Users] ([UserName]) VALUES ('UserName1')
INSERT INTO [Users] ([UserName]) VALUES ('UserName2')
INSERT INTO [Users] ([UserName]) VALUES ('UserName3')
INSERT INTO [Users] ([UserName]) VALUES ('UserName4')
GO
INSERT INTO #UserGUIDs ([UserId]) SELECT [Id] FROM [Users]
GO
--<< Populate Addresses
CREATE TABLE #AddressGUIDs ([AddressId] uniqueidentifier)
GO
INSERT INTO harry.[Addresses] ([Address1]) VALUES ('1234 First Street')
INSERT INTO harry.[Addresses] ([Address1]) VALUES ('2345 Second Street')
INSERT INTO harry.[Addresses] ([Address1]) VALUES ('3456 Third Street')
INSERT INTO harry.[Addresses] ([Address1]) VALUES ('4567 Fourth Street')
GO
INSERT INTO #AddressGUIDs ([AddressId]) SELECT [Id] FROM harry.[Addresses]
GO
PRINT 'Users'
SELECT * FROM [Users]
PRINT 'Addresses'
SELECT * FROM harry.[Addresses]
GO
--<< ========================== TEST ==========================
--<< Populate UserAddresses
INSERT INTO UserAddresses (UserId, AddressId)
SELECT
u.Id, -- UserID
a.Id -- AddressID
FROM harry.Addresses AS a
CROSS JOIN Users AS u
GO
PRINT 'UserAddresses'
SELECT * FROM [UserAddresses]
GO
Result
Msg 547, Level 16, State 0, Line 4
The INSERT statement conflicted with the FOREIGN KEY constraint "FK569ABB5045EE0940". The conflict occurred in database "RGTest1", table "dbo.Addresses", column 'Id'.
Original Post
queen3, here is a complete working example of what I think you're attempting. I tried to make it SQL 2000-compatible, but I only have 2005 and 2008 available.
Please create a new database and run this script. If it does not duplicate what you're trying to do, please explain or just post modified code.
This script works as-is, but I'm sure there is something that is different from your application.
Rob
Code
SET NOCOUNT ON
GO
--<< ========================== DROPS ==========================
IF OBJECT_ID('tempdb..#UserGUIDs') IS NOT NULL
DROP TABLE #UserGUIDs
GO
IF OBJECT_ID('tempdb..#AddressGUIDs') IS NOT NULL
DROP TABLE #AddressGUIDs
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('UserAddresses'))
DROP TABLE [UserAddresses]
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('Users'))
DROP TABLE [Users]
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE [id] = OBJECT_ID('Addresses'))
DROP TABLE [Addresses]
GO
--<< ========================== TABLES ==========================
--<< Users
CREATE TABLE [Users] (
[Id] uniqueidentifier NOT NULL DEFAULT NEWID() PRIMARY KEY,
[UserName] varchar(10) NOT NULL
) ON [PRIMARY]
GO
--<< Addresses
CREATE TABLE [Addresses] (
[Id] uniqueidentifier NOT NULL DEFAULT NEWID() PRIMARY KEY,
[Address1] varchar(20) NOT NULL
) ON [PRIMARY]
GO
--<< UserAddresses
CREATE TABLE [UserAddresses] (
[UserId] uniqueidentifier NOT NULL,
[AddressId] uniqueidentifier NOT NULL,
CONSTRAINT [FK569ABB5045EE0940] FOREIGN KEY ([AddressId]) REFERENCES [Addresses] ([Id]),
CONSTRAINT [UserAddressesToAddressFK] FOREIGN KEY ([UserId]) REFERENCES [Users] ([Id])
) ON [PRIMARY]
GO
--<< ========================== DATA ==========================
--<< Populate Users
CREATE TABLE #UserGUIDs ([UserId] uniqueidentifier)
GO
INSERT INTO [Users] ([UserName]) VALUES ('UserName1')
INSERT INTO [Users] ([UserName]) VALUES ('UserName2')
INSERT INTO [Users] ([UserName]) VALUES ('UserName3')
INSERT INTO [Users] ([UserName]) VALUES ('UserName4')
GO
INSERT INTO #UserGUIDs ([UserId]) SELECT [Id] FROM [Users]
GO
--<< Populate Addresses
CREATE TABLE #AddressGUIDs ([AddressId] uniqueidentifier)
GO
INSERT INTO [Addresses] ([Address1]) VALUES ('1234 First Street')
INSERT INTO [Addresses] ([Address1]) VALUES ('2345 Second Street')
INSERT INTO [Addresses] ([Address1]) VALUES ('3456 Third Street')
INSERT INTO [Addresses] ([Address1]) VALUES ('4567 Fourth Street')
GO
INSERT INTO #AddressGUIDs ([AddressId]) SELECT [Id] FROM [Addresses]
GO
PRINT 'Users'
SELECT * FROM [Users]
PRINT 'Addresses'
SELECT * FROM [Addresses]
GO
--<< ========================== TEST ==========================
--<< Populate UserAddresses
INSERT INTO UserAddresses (UserId, AddressId)
SELECT
u.Id, -- UserID
a.Id -- AddressID
FROM Addresses AS a
CROSS JOIN Users AS u
GO
PRINT 'UserAddresses'
SELECT * FROM [UserAddresses]
GO
Random thought...
What credentials are you using, what did ORM use, and what schema?
eg the tables and FKs are actually using "bob" schema
bob.Addresses
bob.Users
bob.UserAddresses
but because of pre-SQL 2005 user/schema stuff, you are in "harry" schema...
INSERT INTO UserAddresses (UserId, AddressId)
SELECT UserId, Id
FROM Addresses
-- is actually
INSERT INTO harry.UserAddresses (UserId, AddressId)
SELECT UserId, Id
FROM bob.Addresses
-- or
INSERT INTO bob.UserAddresses (UserId, AddressId)
SELECT UserId, Id
FROM harry.Addresses
More than once I've enjoyed the spectacle of testers and developers getting different results because of lack of qualifying schema...
Check the UserAddresses table. Maybe someone has defined a (BAD!) trigger on the table that does some Evil to the Addresses or UserAddresses table somehow.
Related
I want to implement unique key constraint on my table for new values, previous data is duplicate
e.g
declare #temp table
(
id int identity,
PolicyNO varchar(30)
)
insert into #temp values('abc')
insert into #temp values('abc')
insert into #temp values('abc')
now I want to add a unique constraints on #temp table. The new record on id=3 or greater will have to be unique and here third record should be restricted for insertion
ALTER TABLE #TEMP
ADD CONSTRAINT Unique_PolicyNo UNIQUE (PolicyNO ) where id>3
some thing like that I am facing trouble in syntax any help will be appreciated.
Try this:
CREATE TABLE dbo.temp
(
id int identity,
PolicyNO varchar(30)
)
insert into dbo.temp values('abc')
insert into dbo.temp values('abc')
insert into dbo.temp values('abc')
CREATE UNIQUE NONCLUSTERED INDEX [IX__tem]
ON dbo.temp (PolicyNO)
WHERE id>3
Then try to insert one new value twice.
or
CREATE UNIQUE NONCLUSTERED INDEX [IX__tem]
ON dbo.temp (PolicyNO)
WHERE id>3 AND PolicyNO IS NOT NULL
in order to allow insertion of null values.
Following is the table structure:
CREATE TABLE [User] (
[Id] bigint identity(1,1) not null,
[FirstName] nvarchar(100) not null,
[LastName] nvarchar(100) not null,
[Title] nvarchar(5) null,
[UserName] nvarchar(100) not null,
[Password] nvarchar(100) not null,
[Inactive] bit null,
[Created] Datetime not null,
[Creator] bigint not null,
[Modified] DateTime null,
[Modifier] bigint null
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[Id] Asc
)
);
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_User_Creator]') AND parent_object_id = OBJECT_ID(N'[User]'))
ALTER TABLE [User] ADD CONSTRAINT [FK_User_Creator] FOREIGN KEY([Creator]) REFERENCES [User]([Id])
GO
INSERT INTO [User] (Creator) Values ([Id] ?)
This is a case when table is empty and first user is going to add in table. Otherwise I don't have issue.
How can I insert Id in creator column with insert statement at the same time?
One way could be using Sequence instead of identity column. The below script might serve the same purpose:
CREATE SEQUENCE dbo.useridsequence
AS int
START WITH 1
INCREMENT BY 1 ;
GO
CREATE TABLE [User] (
[Id] bigint DEFAULT (NEXT VALUE FOR dbo.useridsequence) ,
[FirstName] nvarchar(100) not null,
[LastName] nvarchar(100) not null,
[Title] nvarchar(5) null,
[UserName] nvarchar(100) not null,
[Password] nvarchar(100) not null,
[Inactive] bit null,
[Created] Datetime not null,
[Creator] bigint DEFAULT NEXT VALUE FOR dbo.useridsequence ,
[Modified] DateTime null,
[Modifier] bigint null
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[Id] Asc
)
);
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_User_Creator]') AND parent_object_id = OBJECT_ID(N'[User]'))
ALTER TABLE [User] ADD CONSTRAINT [FK_User_Creator] FOREIGN KEY([Creator]) REFERENCES [User]([Id])
GO
INSERT INTO [User]
(
-- Id -- this column value is auto-generated
FirstName,
LastName,
Title,
UserName,
[Password],
Inactive,
Created,
Creator,
Modified,
Modifier
)
VALUES
(
'Foo',
'Bar',
'Title',
'UserName ',
'Password',
0,
GETDATE(),
DEFAULT,
GETDATE(),
1
)
SELECT * FROM [User] AS u
Result :
The short answer is that you can't do this. And I suggest your model is logically flawed in the first place. Do you intend to define all actual database users (e.g., create user ... for login ...) as rows in [Users]? You need to think about that - but the typical answer is no. If the answer is yes, then you don't need the creator column at all because it is redundant. All you need is the created date - for which you probably should have defined a default.
But if you want to do this, you will need to do it in two steps (and you will need to make the column nullable). You insert a row (or rows) with values for the "real" data columns. Then update those same rows with the identity values generated for id. An example showing different ways to do this
use tempdb;
set nocount on;
CREATE TABLE dbo.[user] (
[user_id] smallint identity(3,10) not null primary key,
[name] nvarchar(20) not null,
[active] bit not null default (1),
[created] Datetime not null default (current_timestamp),
[creator] smallint null
);
ALTER TABLE dbo.[user] ADD CONSTRAINT [fk_user] FOREIGN KEY(creator) REFERENCES dbo.[user](user_id);
GO
-- add first row
insert dbo.[user] (name) values ('test');
update dbo.[user] set creator = SCOPE_IDENTITY() where user_id = SCOPE_IDENTITY()
-- add two more rows
declare #ids table (user_id smallint not null);
insert dbo.[user] (name) output inserted.user_id into #ids
values ('nerk'), ('pom');
update t1 set creator = t1.user_id
from #ids as newrows inner join dbo.[user] as t1 on newrows.user_id = t1.user_id;
select * from dbo.[user] order by user_id;
-- mess things up a bit
delete dbo.[user] where name = 'pom';
-- create an error, consume an identity value
insert dbo.[user](name) values (null);
-- add 2 morerows
delete #ids;
insert dbo.[user] (name) output inserted.user_id into #ids
values ('nerk'), ('pom');
update t1 set creator = t1.user_id
from #ids as newrows inner join dbo.[user] as t1 on newrows.user_id = t1.user_id;
select * from dbo.[user] order by user_id;
drop table dbo.[user];
And I changed the identity specification to demonstrate something few developers realize. It isn't always defined as (1,1) and the next inserted value can jump for many reasons - errors and caching/restarts for example. Lastly, I think you will regret naming a table with a reserved word since references to it will require the use of delimiters. Reduce the pain.
I recently changed my database structure and now I want to copy from my old table Transfers to the new ones I just created (Orders and Orders_Transfer):
--old table to copy from
-- table 'Transfers'
CREATE TABLE [dbo].[Transfers] (
[Id] int IDENTITY(1,1) NOT NULL,
[Date] datetime NOT NULL,
[Memo] nvarchar(max) NULL,
[Employee_Id] int NULL,
[InventoryFrom_Id] int NOT NULL,
[InventoryTo_Id] int NOT NULL,
);
-- new tables to copy to
-- table 'Orders'
CREATE TABLE [dbo].[Orders] (
[Id] int IDENTITY(1,1) NOT NULL,
[Date] datetime NOT NULL,
[Memo] nvarchar(max) NULL,
[Employee_Id] int NULL
);
-- Creating table 'Orders_Transfer'
CREATE TABLE [dbo].[Orders_Transfer] (
[Id] int NOT NULL,--foreign key on Orders.Id
[InventoryFrom_Id] int NOT NULL,
[InventoryTo_Id] int NOT NULL
);
I want to iterate through the old Transfers table and copy some part of it to Orders (Date, Memo, Employee) and the rest to Orders_Transfer (InventoryFrom_Id, InventoryTo_Id). The Id column in Orders_Transfer is also a FK on Orders.Id so I want to copy the auto-generated value as well.
I read about the scope_identity() function and the OUTPUT clause, but I’m a beginner to SQL and can’t put it all together.
I’m using SQL Server 2008
What is the query I need to do this? Any help would be appreciated, thanks!
I would create an OldId column on the Orders table to store the old primary key:
ALTER TABLE [dbo].[Orders] ADD [OldId] INT;
Then copy in the old data:
INSERT INTO [dbo].[Orders]
(
[OldId],[Date],[Memo],[EmployeeID]
)
SELECT [Id] AS [OldId],[Date],[Memo],[EmployeeID]
FROM [dbo].[Transfers];
Copy the remaining data using the OldId:
INSERT INTO [dbo].[Orders_Transfer]
(
[Id],
[InventoryFrom_Id],
[InventoryTo_Id]
)
SELECT
O.Id, T.[InventoryFrom_Id], T.[InventoryTo_Id]
FROM [dbo].[Orders] O
INNER JOIN [dbo].[Transfers] T
ON O.[OldId] = T.[Id];
And drop the OldId column:
ALTER TABLE [dbo].[Orders] DROP COLUMN [OldId];
To keep the Id values in sync you are going to need to use IDENTITY_INSERT.
SET IDENTITY_INSERT dbo.Orders ON;
/* Insert data into the Orders table */
INSERT INTO [dbo].[Orders]
([Id]
,[Date]
,[Memo]
,[Employee_Id])
SELECT Id
, Date
, Memo
, Employee_Id
FROM Transfers;
SET IDENTITY_INSERT dbo.Orders OFF;
/* Insert data into the Orders_Transfer table */
INSERT INTO [dbo].[Ordres_Transfer]
([Id]
,[InventoryFrom_ID]
,[InventoryTo_ID]
SELECT Id
, InventoryFrom_ID
, InentoryTo_ID
FROM Transfers;
Insert into newtable select * from oldtable //if same schema
insert into newtable(col1,col2,col3) select col1,col2,col3 from old table // for different table schema
I'm using SQL Server 2008, and I have a trigger which I want to copy any rows in the My_Table into a archive History_Table table.
How to copy the entire old content of the table into the archive each time someone inserts a new row?
My table structure is
CREATE TABLE [dbo].[Stu_Table]
(
[Stu_Id] [int] NOT NULL,
[Stu_Name] [varchar] (15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Stu_Class] [int] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Stu_Table] ADD CONSTRAINT [PK_Stu_Table] PRIMARY KEY CLUSTERED ([Stu_Id]) ON [PRIMARY]
GO
My archive table structure is
CREATE TABLE [dbo].[Stu_TableHistory]
(
[Stu_Id] [int] NOT NULL,
[Stu_Name] [varchar] (15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Stu_Class] [int] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Stu_TableHistory] ADD CONSTRAINT [PK_Stu_TableHistory] PRIMARY KEY CLUSTERED ([Stu_Id]) ON [PRIMARY]
GO
My trigger syntax is
Create TRIGGER [dbo].[HistoryKeep]
ON [dbo].[Stu_Table]
INSTEAD OF INSERT
AS
BEGIN
IF((SELECT COUNT(*) FROM Stu_Table WHERE Stu_Id = (SELECT Stu_Id FROM INSERTED)) >= 1)
BEGIN
INSERT INTO dbo.Stu_TableHistory( Stu_Id, Stu_Name, Stu_Class )
SELECT Stu_Id, Stu_Name, Stu_Class FROM Stu_Table WHERE Stu_Id = (SELECT Stu_Id FROM INSERTED)
UPDATE x
SET x.Stu_Name = i.Stu_Name
FROM dbo.Stu_Table AS x
INNER JOIN inserted AS i ON i.Stu_Id = x.Stu_Id
END
ELSE
BEGIN
INSERT INTO dbo.Stu_Table( Stu_Id, Stu_Name, Stu_Class )
SELECT Stu_Id, Stu_Name, Stu_Class FROM INSERTED
END
END
In a word need help to transfer the old data from student table to archive table. My above trigger syntax can not satisfy me.
If have any query plz ask thanks in advance.
Instead of your current trigger, you should have something like:
Create TRIGGER [dbo].[HistoryKeep]
ON [dbo].[Stu_Table]
INSTEAD OF INSERT
AS
BEGIN
DECLARE #History table (
Action sysname not null,
STU_ID [int] NULL,
[Stu_Name] [varchar] (15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Stu_Class] [int] NULL
)
;MERGE INTO Stu_Table t
USING INSERTED i ON t.STU_ID = i.STU_ID
WHEN MATCHED THEN UPDATE SET STU_Name = i.STU_Name
WHEN NOT MATCHED THEN INSERT (STU_ID,STU_NAME,STU_CLASS) VALUES (i.STU_ID,i.STU_NAME,i.STU_CLASS)
OUTPUT $Action,deleted.stu_id,deleted.stu_name,deleted.stu_class INTO #History;
INSERT INTO stu_TableHistory (stu_id,stu_name,stu_class)
select stu_id,stu_name,stu_class from #History where Action='UPDATE'
END
Note, also, that you'll need to drop your current PK constraint on STU_TableHistory, since as soon as a row is updated more than once, there'll be two entries containing the same STU_ID.
As per my comment, this treats INSERTED as a table containing multiple rows throughout. So if Stu_Table contains a row for STU_ID 1, the following insert:
INSERT INTO STU_Table (STU_ID,STU_Name,STU_Class) VALUES
(1,'abc',null),
(2,'def',null)
will update the row for STU_ID 1, insert a row for STU_ID 2, and insert one row into stu_tableHistory (for STU_ID 1)
Let's say my table structure looks something like this:
CREATE TABLE [dbo].[table1] (
[id] [int] IDENTITY(1,1) NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED ([id] ASC)
)
CREATE TABLE [dbo].[table2] (
[id] [int] IDENTITY(1,1) NOT NULL,
[table1_id] [int] NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED ([id] ASC)
)
The [id] field of the first table corresponds to the [table1_id] field of the second. What I would like to do is insert data into both tables in a single transaction. Now I already know how to do this by doing INSERT-SELECT-INSERT, like this:
BEGIN TRANSACTION;
DECLARE #id [int];
INSERT INTO [table1] ([data]) VALUES ('row 1');
SELECT #id = SCOPE_IDENTITY();
INSERT INTO [table2] ([table1_id], [data]) VALUES (#id, 'more of row 1');
COMMIT TRANSACTION;
That's all good and fine for small cases like that where you're only inserting maybe a handful of rows. But what I need to do is insert a couple hundred thousand rows, or possibly even a million rows, all at once. The data is coming from another table, so if I was only inserting it into a single table, it would be easy, I'd just have to do this:
INSERT INTO [table] ([data])
SELECT [data] FROM [external_table];
But how would I do this and split the data into [table1] and [table2], and still update [table2] with the appropriate [table1_id] as I'm doing it? Is that even possible?
Try this:
insert into [table] ([data])
output inserted.id, inserted.data into table2
select [data] from [external_table]
UPDATE: Re:
Denis - this seems very close to what I want to do, but perhaps you could fix the following SQL statement for me? Basically the [data] in [table1] and the [data] in [table2] represent two different/distinct columns from [external_table]. The statement you posted above only works when you want the [data] columns to be the same.
INSERT INTO [table1] ([data])
OUTPUT [inserted].[id], [external_table].[col2]
INTO [table2] SELECT [col1]
FROM [external_table]
It's impossible to output external columns in an insert statement, so I think you could do something like this
merge into [table1] as t
using [external_table] as s
on 1=0 --modify this predicate as necessary
when not matched then insert (data)
values (s.[col1])
output inserted.id, s.[col2] into [table2]
;
I was also struggling with this problem, and find that the best way is to use a CURSOR.
I have tried Denis solution with OUTPUT, but as he mentiond, it's impossible to output external columns in an insert statement, and the MERGE can't work when insert multiple rows by select.
So, i've used a CURSOR, for each row in the outer table, i've done a INSERT, then use the ##IDENTITY for another INSERT.
DECLARE #OuterID int
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT ID FROM [external_Table]
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #OuterID
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [Table] (data)
SELECT data
FROM [external_Table] where ID = #OuterID
INSERT INTO [second_table] (FK,OuterID)
VALUES(#OuterID,##identity)
FETCH NEXT FROM MY_CURSOR INTO #OuterID
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
Keep a look out for SQL Server to support the 'INSERT ALL' Statement. Oracle has it already, it looks like this (SQL Cookbook):
insert all
when loc in ('NEW YORK', 'BOSTON') THEN
into dept_east(deptno, dname, loc) values(deptno, dname, loc)
when loc in ('CHICAGO') THEN
into dept_mid(deptno, dname, loc) values(deptno, dname, loc)
else
into dept_west(deptno, dname, loc) values(deptno, dname, loc)
select deptno, dname, loc
from dept
BEGIN TRANSACTION;
DECLARE #tblMapping table(sourceid int, destid int)
INSERT INTO [table1] ([data])
OUTPUT source.id, new.id
Select [data] from [external_table] source;
INSERT INTO [table2] ([table1_id], [data])
Select map.destid, source.[more data]
from [external_table] source
inner join #tblMapping map on source.id=map.sourceid;
COMMIT TRANSACTION;
Create table #temp1
(
id int identity(1,1),
name varchar(50),
profession varchar(50)
)
Create table #temp2
(
id int identity(1,1),
name varchar(50),
profession varchar(50)
)
-----main query ------
insert into #temp1(name,profession)
output inserted.name,inserted.profession into #temp2
select 'Shekhar','IT'
You could write a stored procedure that iterates over the transaction that you have proposed. The iterator would be the cursor for the table that contains the source data.
Another option is to run the two inserts separately, leaving the FK column null, then running an update to poulate it correctly.
If there is nothing natural stored within the two tables that match from one record to another (likely) then create a temporary GUID column and populate this in your data and insert to both fields. Then you can update with the proper FK and null out the GUIDs.
E.g.:
CREATE TABLE [dbo].[table1] (
[id] [int] IDENTITY(1,1) NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED ([id] ASC),
JoinGuid UniqueIdentifier NULL
)
CREATE TABLE [dbo].[table2] (
[id] [int] IDENTITY(1,1) NOT NULL,
[table1_id] [int] NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED ([id] ASC),
JoinGuid UniqueIdentifier NULL
)
INSERT INTO Table1....
INSERT INTO Table2....
UPDATE b
SET table1_id = a.id
FROM Table1 a
JOIN Table2 b on a.JoinGuid = b.JoinGuid
WHERE b.table1_id IS NULL
UPDATE Table1 SET JoinGuid = NULL
UPDATE Table2 SET JoinGuid = NULL