Inserting row with foreign key relation in same transaction as primary row - sql

I have a 2 tables where one has a foreign key relation to the other
CREATE TABLE foo (
id INT NOT NULL PRIMARY KEY IDENTITY,
value VARCHAR(50) DEFAULT NULL,
);
CREATE TABLE bar (
id INT NOT NULL PRIMARY KEY IDENTITY,
foo_key INT NOT NULL
value VARCHAR(50) DEFAULT NULL,
);
I'm using parameterized ADO.NET ExecuteReader to Insert new rows. My pickle is, if I want to insert 2 rows in different tables in the same transaction, i.e. before commit, I cannot insert rows in bar since I don't know the value that has been given foo.id yet. How would you go about doing that? i.e. How do I make sure that bar.foo_key get assigned the right value? Trying to select on it brings nothing, since I guess it is not actually there yet. Should I use a stored procedure to try and generate the key on the fly, or maybe there is an internal variable that can be used. Or is there a way to have the insert return the new id? Do I need a foreign key declaration, though I'm not sure that would be useful since again I still don't know what id to use?
The reason why I want to do it in one go, is due to error handling, I want to be able to roll everything back in case of an error.

You can use scope_identity() to retrieve the newly generated identity:
begin tran;
insert Foo (value) values ('6*7');
declare #fk int = scope_identity();
insert bar (foo_key, value) values (#fk, '42');
commit tran;
Per HLGEM's comment, to return the value of the newly generated identity to the client, you can use output:
insert Foo (value) output inserted.ID values ('6*7');
Note that for a transaction to span two sessions, you need a distributed transaction, which is very expensive.

I figured out I can return scope_identity() on the insert
INSERT INTO [foo] ([value]) VALUES (#0) SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];

Related

SQL insert ID from IDENTITY ID of the row being inserted

I would like to know, if there is a direct way to insert ID (generated at ID column with IDENTITY(1,1)) to another columns.
In another words, I am looking for SCOPE_IDENTITY() I could get at the time of inserting, not after the INSERT is commited.
I have a table, where there is a column with secondary ID (SID), which references rows from the same table and in some special cases it references itself.
The only way I know to do that is to do the INSERT and consequently UPDATE SID in those cases. Simplified example:
DECLARE #ID INT
INSERT INTO Table (SID) VALUES (NULL);
SELECT #ID = SCOPE_IDENTITY();
UPDATE Table SET SID = ID WHERE ID = #ID;
There are some glitches, i.e. due to the fact that the row may or may not reference itself, etc.
You can do this with an AFTER INSERT trigger. In case of self-reference, leave the column NULL and have the trigger set the column equal to the IDENTITY column.
In pseudo:
Join the table with inserted, filter where SID is NULL
For those rows, update the table and set SID = ID
If it is not possible to use the NULL value, in cases where it should be possible to have no reference at all, you can use another stub value. E.g. -1 if the IDs will always be positive. In that case, apply the above way of working and substitute NULL with -1.

Using ##identity for consecutive inserts

I have this situation,
INSERT INTO TABLE1()...
--Get the primary key from the above insert
SELECT ##identidy
INSERT INTO TABLE2()...
The auto generated primary key has to be a foreign key in TABLE 2. How can I construct my second INSERT to have the value of ##identity?
This doesn't seem to work,
INSERT INTO TABLE1 (user_id, name) (##identity, 'ABC')
I get an error saying Must declare variable '##identidy'.
Cheers!!
1) you spelled ##identity wrong (##identidy)
2) You should create a local variable (#LastIdentity) to store the last inserted identity immediately after the first insert. Then use that variable as the input to the second INSERT:
DECLARE #LastIdentity int
INSERT INTO TABLE1()...
--Get the primary key from the above insert
SELECT #LastIdentity = ##identity
INSERT INTO TABLE2(...) VALUES (#LastIdentity, ...

sql: insert object spread over multiple tables

There are already posts, for example this one, which state that "naive" inheritance in SQL, namely using one table per class level, is a common thing. Example
create table parent
( id integer primary key
, more-parent-attributes
);
create table child
( id integer primary key references parent(id) on delete cascade
, more-child-attributes
);
My question is only how to insert a child in an idiomatic ANSI SQL way
into the table. The foreign key constraint makes the requirement that we first
insert a new row into parent and then a new row into child, using the id
of the parent row. I don't know how to do this (get this id) safely and portably, and
using only one request.
Hint: I'm rather a beginner and don't know imperative SQL programming--just in
case there is an obvious imperative solution.
You must execute two insert.
The first insert add row in parent table, the second insert add row in the child table.
Two insert operations can be grouped in the same transaction.
To get the correct inserted id in the parent table you must get a select id from parent.
Show below:
Step 1:
INSERT INTO parent (id, more att) values (your ID, other values)
Pay attention about ID value, you can use newid() (Sql server) uuid() (mySql) or autoincremental integer field
Step 2:
You retrieve your key querying your parent table with a functional key.
SELECT id FROM parent where functional_key satisfacted
For example, if I store in my parent table a list of employes, a functional key can be register number.
So your query becomes:
SELECT id FROM parent WHERE register_no = 'YOUR_REGISTER_NUMBER'
Step 3:
INSERT INTO child (id, fk_parent, other fields) values(id, fk_parent, other fields)
The fk_parent field must be valued with the result of Step 2.
In this step you can:
value fk_parent with a variable or you can use a subquery (step 2) in your insert statement.
I ended up doing something similar. You need to have some identifying piece of data that you can insert into the Parent in order to get the Id. If you're using this in some kind of application then you can use a GUID. In my application I used a concatenation of source columns that I knew would produce a unique value.
CREATE TABLE Parent
(
Id INT IDENTITY NOT NULL PRIMARY KEY
,SourceId VARCHAR(50) NOT NULL
);
CREATE TABLE Child
(
ParentId INT NOT NULL REFERENCES Parent (Id)
,Data VARCHAR(20)
);
-- Some procedure inserts the unique value
INSERT INTO Parent (SourceId) VALUES ('UNIQUE VALUE');
-- Another procedure inserts data using the unique value
DECLARE #Id INT;
SELECT #Id = Id FROM Parent WHERE SourceId = 'UNIQUE VALUE';
INSERT INTO Child (ParentId, Data) VALUES (#Id, 'Some Data');
Scope_Identity() is what you looking for:
DECLARE #Id INT
INSERT INTO parent (more-parent-attributes) values (.....)
SET #Id = Scope_Identity()
INSERT INTO child (parent(id), more-child-attributes) SELECT #Id, ....more-child-attributes
Scope_Identity() returns identity column in the same scope. It means that Parent key should be Identity column:
id int IDENTITY(1,1)PRIMARY KEY
and I think this is the case as if you were deciding what is the Parent key id, you would use the same for child insert.

Get last UNIQUEIDENTIFIER inserted in a table inside a TRIGGER from a SQL 2005 database

I need to be able to monitor a Table and react very time a record is inserted. This table has no ITN IDENTITY field, only a UNIQUEIDENTIFIER as its primary key. Without any alteration of existing inputs, SPs, etc. I need to be able to find the last inserted ID from within a trigger. This is what I have (obviously does not work):
CREATE TRIGGER TR_UserInserted
ON Users
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
EXEC UserInserted (SELECT User_Id FROM INSERTED);
END
GO
Here I am trying to get the User_Id from the last inserted record in the Users table and run it through the UserInserted SP. Thank you for the help, I am stumped.
HLGEM Made a great point - even on a bulk insert, I only need the last record inserted - I know this is a strange request.
You need to change your trigger to fire INSTEAD OF INSERT. A uniqueidentifier variable must be generated using the NEWID() function. In the INSERT statement in the trigger body, the columns must be provided in order. Assuming a table defined this way:
CREATE TABLE Users (
First int,
User_Id uniqueidentifier PRIMARY KEY,
Third int,
Fourth int)
Then the trigger is:
CREATE TRIGGER TR_UserInserted ON Users
INSTEAD OF INSERT AS
BEGIN
DECLARE #newid uniqueidentifier = NEWID()
INSERT INTO Users
SELECT
First,
#newid,
Third,
Fourth
FROM inserted
EXECUTE UserInserted(#newid)
-- you can actually provide all the columns to UserInserted
END
For this to work properly, make sure that the table does not have a default for the primary key as NEWID().

How do I insert into a table and get back the primary key value?

I have a primary key set up to auto increment.
I am doing multiple queries and I need to retrieve that primary key value to use as a foreign key in another table (IsIdentity = TRUE).
Is there any elegant way to get back the primary key value when I do an insert query? Right now I am requerying and getting the highest value in that column which seems really hacky.
Any suggestions?
If you are using SQL Server 2005 or later, you can use the OUTPUT clause.
create table T(
pk int identity primary key,
dat varchar(20)
);
go
insert into T
output inserted.pk
values ('new item');
go
drop table T;
The output can be directed to a table as well as to the client. For example:
create table T(
pk int identity primary key,
dat varchar(20)
);
create table U(
i int identity(1001,1) primary key,
T_pk int not null,
d datetime
);
go
insert into T
output inserted.pk, getdate()
into U(T_pk,d)
values ('new item'), ('newer item');
go
select * from T;
select * from U;
go
drop table T, U;
Beginning with SQL Server 2008, you can use "composable DML" for more possibilities.
insert into YourTable values (...)
get the new PK with scope_identity()
select scope_identity()
INSERT INTO YourTable (1, 2, etc.)
OUTPUT inserted.yourIDcolumn
VALUES (value1, value2, value...)
Note: This is for MS SQL 2005 and greater
SCOPE_IDENTITY() is probably what you want. It returns the ID of the last record inserted by the same code context in which it executes.
IDENT_CURRENT('tablename') is subject to concurrency issues. That is, there's no guarantee that another record won't be inserted between the INSERT and the call to IDENT_CURRENT.
I must confess, I'm not sure to what source of amazement the VillageIdiot's outburst refers, but I myself am quite astonished that this question does not appear to be a duplicate at all.
holy crap!!!
just call SCOPE_IDENTITY() function:
insert into your_talble(col1,col2) values('blah','more blah')
select scope_identity()
because selecting highest value will return error if any other statement make an insert. the function scope_identity() returns the identity created in current context (that is by your statement)
You should use scope_identity(). And I recommend to wrap insert statement and scope_identity() into transaction.