Select from another table within an Insert Trigger - sql-server-2005

I am maintaining an audit table, where in I have a parent table and it's child table.I want to insert the primary key of the parent audit table into it's child audit table.
Should I be declaring a "before insert" instead of a "for insert" trigger. Here's my code:
CREATE trigger [trgAudtblChild] On [tblChild]
for Insert
as
BEGIN
declare #serNo bigint
declare #expSerNo int
declare #postPercent numeric (12, 2)
declare #prdSAPid varchar (50)
declare #lastUpdatedBy int
declare #lastUpdatedOn smalldatetime
SELECT
--#serno = serno,
#expSerNo = expSerNo ,
#postPercent = postPercent ,
#prdSAPid = prdSAPid ,
#lastUpdatedBy = lastUpdatedBy ,
#lastUpdatedOn = lastUpdatedOn
FROM INSERTED
select #serno = max(at_serno) from AT_tblParent
insert into AT_tblChild(serNo, expSerNo, postPercent
, prdSAPid, lastUpdatedBy, lastUpdatedOn
, change_column_index) values(
#serNo, #expSerNo, #postPercent
, #prdSAPid, #lastUpdatedBy, #lastUpdatedOn
, 'INSERTED')
End
Return
The above code, does not work and puts the table into transaction.

Before Trigger - When you want to Intercept the data before it actually gets Inserted in Table.
For Trigger - Your record is Inserted but can still modify it.
The only difference is that about record is actually Inserted or not.
Back to the original Query
In you above mentioned situation, you should not use Before Trigger. Consider a case, when your Parent Table record Insertion in under some Transaction and same Transaction gets Rollbacked. In that case, It will crash for the Foreign key constraint. Because you will try to Reference a Foreign key Record of Parent Table into Child Table during Insertion which does not exist in Parent Table.

Related

SQL Server : primary key violation on occasion despite prior deletion

I have a procedure that is supposed to replace an entry in my table by first deleting it based on an id and then inserting it with new values.
It looks like this, plus additional values used for the insert statement that I have left out.
ALTER PROCEDURE [dbo].[DemoProc]
#id BIGINT,
...
AS
IF EXISTS (SELECT id FROM demoTable WHERE id = #id)
DELETE demoTable
WHERE id = #id
INSERT INTO demoTable (id, ...)
VALUES (#id, ...)
RETURN 0
Now every now and again, I'm getting an error log that alerts me of a primary key violation that originates from this procedure. I also end up with the entry not being inserted at all. Any ideas?

Trigger on View not firing on SQL Server 2012

I have seen some articles mention the possibility of a Trigger on a View, triggering on either insert, updates or deletes to one of the base tables from which the View is created.
However I am not able to get a simple example to work.
CREATE TABLE [Test].[Data] (
Id INT PRIMARY KEY IDENTITY (1,1),
Data VARCHAR(255) NOT NULL,
);
GO
CREATE VIEW [Test].[View] AS SELECT * FROM [Test].[Data];
GO
CREATE TABLE [Test].[Queue] (
Id INT PRIMARY KEY IDENTITY (1,1),
DataId INT NOT NULL,
Action VARCHAR(255) NOT NULL,
Timestamp DATETIME NOT NULL,
);
GO
CREATE TRIGGER InsertTrigger ON [Test].[View] INSTEAD OF INSERT AS
BEGIN
DECLARE #DataId INT;
DECLARE #Timestamp DATETIME;
SET #DataId = (SELECT Id FROM INSERTED);
SET #Timestamp = GETDATE();
INSERT INTO [Test].[Queue] (DataId, Action, Timestamp) VALUES (#DataId, 'Insert', #Timestamp)
END
GO
ENABLE TRIGGER InsertTrigger ON [Test].[View];
GO
INSERT INTO [Test].[Data] (Data) VALUES ('Testdata');
The trigger is not firing, is the above not possible or is there something wrong with my Sql?
Edit: Although answered I would like to clarify the question. The idea was to get the trigger on the View to fire, when there was an Insert to the base table and not the View itself.
A trigger on a view will only work on inserts into that view, not on any inserts into tables to which the view references.
In your script you're not inserting into that view, you're inserting into a table.
In addition to not testing this correctly, your view is wrong. You are not considering that inserted represents multiple rows, not one.
So:
CREATE TRIGGER InsertTrigger ON [Test].[View] INSTEAD OF INSERT AS
BEGIN
INSERT INTO [Test].[Queue] (DataId, Action, Timestamp)
SELECT i.Id, 'Insert', GETDATE()
FROM Inserted;
END;
GO
INSERT INTO [Test].[View] (Data)
VALUES ('Testdata');

SQL Server: how to insert a record into related table during an update?

Let's say we have two tables, Incidents and IncidentTracking:
Incidents (Id INT PRIMARY KEY,
CreatedOn Datetime,
State VARCHAR(50))
IncidentTracking (Id INT PRIMARY KEY,
IncidentId INT FOREIGN KEY REFERENCES TO Incidents.Id,
TrackingDate Datetime,
NewState VARCHAR(50))
How do I insert a new record into IncidentTracking while updating some incidents?
For example, I want to change the State for all incidents that are more than 90 days old to "Outdated", and insert a tracking info record where IncidentId is the updated incident (SCOPE_IDENTITY() maybe?), TrackingDate is GETDATE() and NewState is also "Outdated".
Can it be done all in one statement or should I write a cursor?
I'd use OUTPUT clause.
As IncidentTracking has a foreign key, it is not possible to OUTPUT directly to it. You'll get an error message if you try:
The target table 'dbo.IncidentTracking' of the OUTPUT INTO clause
cannot be on either side of a (primary key, foreign key) relationship.
Found reference constraint 'FK_IncidentTracking_Incidents'.
So, we can use a temporary table or table variable.
Like this:
BEGIN TRANSACTION;
DECLARE #T TABLE (IncidentId int, TrackingDate datetime, NewState varchar(50));
UPDATE [dbo].[Incidents]
SET [State] = 'Outdated'
OUTPUT
inserted.Id AS [IncidentId],
GETDATE() AS [TrackingDate],
inserted.[State] AS [NewState]
INTO #T ([IncidentId], [TrackingDate], [NewState])
WHERE [CreatedOn] < DATEADD(day, -90, GETDATE())
;
INSERT INTO [dbo].[IncidentTracking] ([IncidentId], [TrackingDate], [NewState])
SELECT [IncidentId], [TrackingDate], [NewState]
FROM #T;
COMMIT TRANSACTION;
Write a stored procedure to perform your task. You can put that kind of logic in a stored procedure easily enough.
If you will allow access to the table(s) outside of the procedure and still want the same behavior, a trigger is likely what you want (not a fan of them myself). Make sure, when writing your trigger, you remember that it will run against a recordset not a single record.
You could have a trigger on the state field that inserts into IncidentTracking, then you just need the one update. Or the other way around (adding to IncidentTracking updates State)
But why even have the State in the Incidents table? Just have an IncidentState table where you add every state change, and the latest added one is the current state for the incident.
You'd also probably want to make the State field a StateId column instead of a text code. Connected to a State table containing all the states.

Inserting one table's primary key into another table

I have two tables in SQL Server, Appointment and AppointmentDetails.
Appointment table has two columns AppId and CusId.
AppointmentDetail table has AppId, ApDay, Intime, OutTime, EmpId.
Appointment table has AppId as its primary key, and it is set to auto increment.
AppointmentDetails table has primary key on (AppId, ApDay).
My problem how get and insert the primary key of the Appointment table to AppointmentDetails table when I am inserting data into Appointment table???
Here's one method of doing it (with corrected spellings for table names though I accept these are sometimes beyond your control):
DECLARE #insertedId INT;
BEGIN TRANSACTION
INSERT INTO Appointment(CusId) VALUES(#cusId);
SET #insertedId = SCOPE_IDENTITY();
COMMIT
BEGIN TRANSACTION
INSERT INTO
AppointmentDetails
SELECT
AppId = #insertedId
,ApDay = #apDay
,Intime = #inTime
,OutTime = #outTime
,EmpId = #empId
FROM
Appointment
COMMIT
Alternatively you could use a trigger, but they're just evil!
I think they asking how can they return the new key that was generated to then insert it into the details table.
Check this post out if using JDBC but the idea is the same for all languages: How to get the insert ID in JDBC?

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.