Enforce inserting a record into a secondary table every time a record is inserted into a primary table - sql

In a SQL Server 2008 R2 setup, I have table1 (id, col1-1, col1-2, ...) and table2 (id, table1_id, col2-1, col2-2, ...), where table2.table1_id points to table1.id (i.e. a foreign key to primary key relation).
How do I enforce the rule that every time somebody insert a record into table1, they are also required to create a record into table2?
I would guess that the obvious mechanism to use here are triggers, but this can lead to the chicken and the egg problem since the trigger will require a record in table2 in order to create a record in table1, but in order to create a record in table2 first I need the table1.id value to already exists. How to overcome this?
I need the enforcement to be automatic and built-in into the database. A frequently running job to check for records in table1 without corresponding records in table2 will not make the cut and the rule must be enforced at real time when somebody is actually creating a record in table1.

You could simply use OUTPUT clause to do that. Here is a simple example
CREATE TABLE Parent(Id INT, Col VARCHAR(45));
CREATE TABLE Child(Id INT, Col VARCHAR(45));
INSERT INTO Parent(Id, Col)
OUTPUT INSERTED.Id, INSERTED.Col INTO Child
VALUES(1, 'SomeValue');
SELECT *
FROM Parent P
JOIN Child C ON P.Id = C.Id;
Note that the extended support for SQL Server 2008/2008 R2 ends on July 9, 2019. You should upgrade as soon as possible.

Chicken and the egg problem is preventable, but I tested on Windows SQL 2016:
CREATE TRIGGER [dbo].[Table1_Insert]
ON [dbo].[Table1RR] AFTER INSERT
AS
IF rowcount_big() = 0 RETURN --leave trigger if no record (-s) inserted in the Table1
SET NOCOUNT ON
ALTER TABLE dbo.Table2RR NOCHECK CONSTRAINT FK_Table2_Table1 -- resolve chicken and the egg problem
INSERT INTO dbo.Table2RR select id,null,null from inserted order by id
ALTER TABLE dbo.Table2RR CHECK CONSTRAINT FK_Table2_Table1
GO
ALTER TABLE [dbo].[Table1RR] ENABLE TRIGGER [Table1_Insert]
GO

Related

Relating two tables

I have created tables T1 with columns( id as Primary key and name) and T2 with columns( id as primary key, name, t_id as foreign key references T1(id)) . I Inserted some values from inputs from a Windows form. After querying SELECT * FROM T2; using isql, all the values in the foreign key column are null instead of duplicating values in T1(id) because of the relationship created. Is they anything I have left out or need to add? The primary key of both tables are autoincremented.
You are confusing auto-incremented keys and relationship uses.
Auto-incremented keys (or generally talking, fields) just help you when you are inserting a new record on the table of the key. But when you are inserting a new record that makes a reference to a record in another table, then you must specify that record, using the foreign key field. Or in your case, the user that is inserting the "name" in T2 must say which one record on T1 that "name" in T2 is making a reference.
Your confusion on the relationship is that you are thinking that an established relationship will enforce the use of that values automatically. But the relationship just enforce the validation of the values. So, the field t_id in T2 will not use the value of the last record of T1 automatically. But if you try to insert a value that do not exist in T1 in the field t_id, the relationship will not let you do.
So, answering your question, what you left out and need to add?
You left out the part of the code that insert the value on the t_id field of T2 table.
Let me try to explain using an example that is more common.
The most common case of this is that the application insert first the T1 record and then when the user is inserting T2, the application provide a way to the user to choose which one T1 record his T2 record is referencing.
Suppose T1 is a publishers table and T2 is a book table. User insert a publisher, and when it is inserting a book it can choose which one publisher publish that book.
Field "ID" of Customers will be AUTOINCREMENT by default in table create using Event BeforeInsert on table CUSTOMERS. LOOK AT
CREATE TRIGGER nametrigger FOR nametable
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (NEW.ID IS NULL) THEN BEGIN
NEW.ID = GEN_ID(GEN_PK_ID, 1);
END
END
Now one new record in Customers
INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country)
VALUES ('Cardinal','Tom B. Erichsen','Skagen 21','Stavanger','4006','Norway');
Then ID will be automaticaly one sequencial number from 1 up to last integer or smallint or bigint as you defined in your create table (pay attencion that ID field is not include in FIELDS and VALUES) because TRIGGER
now you can use the dataset (obj) options to link the table MATER and DETAIL see in help delphi
or in SQL you can to use PARAMS FIELDS
later insert one new record in table MASTER try...
INSERT INTO xTable2 (IDcustomersField, ..., ..., ...., ....)
VALUES ( :IDcustomersField, ..., ..., ...., ....);
xTable2 may using one field ID (Primary Key) autoincrement too. this help when DELETING or UPDATING fileds in this table
Then you can say the value to :IDcustomersField in table detail using
xQuery.PARAM( 0 ).value or xQuery.PARAMBYNAME( IDcustomersField).value (here im using Query obj as example )
you can to use example with DATASOURCE in code to say the value for IDcustomersField
can to use
Events in SQL
can to use
PROCEDURE IN SQL
DONT FORGOT
you have to create Relationship between two table ( REFERENCIAL INTEGRITY and PRIMARY KEY in mater table ) NOT NULL FOR TWO FIELDS ON TABLES
I believe that understand me about my poor explanation (i dont speak english
You need to insert the values for t_id manually, after you get the ID's value from the main table T1.
Depending on your logic in the database you also can use a trigger or a stored procedure. Give us more information about what values you expect to have in NAME field in T2 after the insert? Are they duplicates from T1 or independent from T1?
If T1.NAME=T2.NAME, you can automate the process with a trigger
CREATE OR ALTER TRIGGER TR_T1_AI0 FOR T1
ACTIVE AFTER INSERT POSITION 0
AS
BEGIN
INSERT INTO T2(NAME, T_ID)
VALUES (NEW.NAME, NEW.ID);
END
If T2.NAME's value is different from T1.NAME you can use a stored procedure with parameters both names:
CREATE ORA ALTER PROCEDURE XXXX(
P_NAME_T1 TYPE OF T1.NAME,
P_NAME_T2 TYPE OF T2.NAME)
AS
DECLARE VARIABLE L_ID TYPE OF T1.ID;
BEGIN
INSERT INTO T1(NAME)
VALUES (:p_NAME_T1)
RETURNING ID INTO:L_ID;
INSERT INTO T2(NAME, T_ID)
VALUES (:P_NAME_T2, :l_ID);
END
You can use both statements from the stored procedure directly in your program if it supports the returning syntax. If not, you need an additional query with SELECT NEXT VALUE FOR GENERATOR_FOR_T1 FROM RDB$DATABASE; and use the value returned from it in both INSERT statements.

Creating a Trigger which will insert record in a table on update of another table

Suppose I have tables T1 and T2
Columns of T1 -->Value
Columns of T2 -->OldValue NewValue
What I require is a trigger which will insert a record in T2 on updation of T1 , I need to know the old value and new value also , I have never used triggers before , so can any help me with this , how do I go about creating this trigger.Is it possible ,thanks.
Well, you start writing a trigger with CREATE TRIGGER:
CREATE TRIGGER NameOfTheTriggerPlease
…
The table that should trigger the additional action is T1 so the trigger should be defined ON that table:
CREATE TRIGGER T1OnUpdate /* that's just an example,
you can use a different name */
ON T1
…
The action that the trigger should be invoked on is UPDATE and the timing is AFTER the update, so…
CREATE TRIGGER T1OnUpdate
ON T1
AFTER UPDATE
…
Now's the time to introduce the body of the trigger, i.e. the statements that should actually be executed by the trigger. You introduce the body with the AS keyword followed by the statements themselves.
In your case, there would be just one statement, INSERT, which is obvious. What's not so obvious is how we are going to access the old and the new values. Now, SQL Server offers you two virtual tables, INSERTED and DELETED, and you can easily guess that the former contains all the new values and the latter the old ones.
These tables have the same structure as the table the trigger is assigned to, i.e. T1. They only contain rows that were affected by the particular UPDATE statement that invoked the trigger, which means there may be more than one. And that, in turn, means that you need to have some primary key or a unique column (or a set of columns) in your T1 table that you can use in the trigger to match deleted and inserted rows. (In fact, you might also need your T2 table to have a column that would reference the T1's primary key, so you could later establish which row of T1 had which values stored in T2.)
For the purposes of this answer, I'm going to assume that there's a primary key column called PK and a foreign key column of the same name in T2. And the INSERT statement then might look like this:
CREATE TRIGGER T1OnUpdate
ON T1
AFTER UPDATE
AS
INSERT INTO T2 (PK, OldValue, NewValue)
SELECT i.PK, i.Value, d.Value
FROM INSERTED i INNER JOIN DELETED d ON i.PK = d.PK
One last (but not least) thing to remember: the entire CREATE TRIGGER statement should be the only one in the batch, i.e. there should be no statements preceding the CREATE TRIGGER keywords (but you can put comments there) and, likewise, everything after the AS keyword is considered part of the trigger's body (but you can put the GO delimiter to indicate the end of the statement if you are running the script in SQL Server Management Studio, for instance).
Useful reading:
CREATE TRIGGER (Transact-SQL)
I'm not going to build the whole thing for you (no fun, right?) but I can point you in the right direction
create trigger logUpdate
on T1
After update
as
begin
insert into T2...
--here is just an example
select * from deleted --the DELETED table contains the OLD values
select * from inserted --the INSERTED table contains the NEW values
end
remember that DELETED and INSERTED are internal tables that contains old and new values. On a update trigger, they both exist. On a insert trigger, DELETED will be null because there is nothing being delete. Same logic on a delete trigger, the INSERTED will be empty
EDIT:
answering your question: no matter how many fields you update, your DELETED and INSERTED tables you have all the columns of all the rows affected. Of course, if you update only one column, all the other will have the same value on DELETED and INSERTED
create trigger T_UPD_T1
on T1 FOR update
as
insert into T2 select deleted.value, inserted.value from inserted, deleted

Database Triggers: On Insert

This is a simple example.
I want to insert data in Table1 (Name, Age, Sex). This table has an automatically increasing serial#(int) on insertion of data.
I want to put a trigger on Table1 insert, so that after inserting data, it picks up the serial#(int) from Table1 and puts Serial# and Name to Table2 and Serial# and some other data in Table3.
Is it possible via triggers?
or, should I pick (last) Serial from table1 and call insert on other tables by increasing it manually, in same SP I used to insert in Table1?
Which approach is better?
EDIT 1:
Suppose table:
Serial | UID | Name | Age | Sex | DateTimeStamp
(int | uniqueidentifier | nvarchar | smallint | nchar | DateTime )
Default NewID() and Default GetDate() as UID and DateTimeStamp, would INSERTED table have Datetime-Of-Insertion in DatetimeStamp field? Meaning, I originally didn't enter any of Serial, GUID or DatetimeStamp, will they occur in INSERTED table?
EDIT 2:
Can you point me towards good books/articles on triggers. I read mastering SQL server 2005, didn't get much out of it. Thanks!
Sure you can do this with a trigger - something like:
CREATE TRIGGER trg_Table1_INSERT
ON dbo.Table1 AFTER INSERT
AS BEGIN
INSERT INTO dbo.Table2(SerialNo, Name)
SELECT SerialNo, Name
FROM Inserted
INSERT INTO dbo.Table3(SomeOtherCol)
SELECT SomeOtherCol
FROM Inserted
END
or whatever it is you need to do here....
It's important to understand that the trigger will be called once per statement - not once per row inserted. So if you have a statement that inserts 10 rows, your trigger gets called once, and the pseudo-table Inserted will contain those 10 rows that have been inserted in the statement.
Yes, this is possible with triggers.
When you use an INSERT trigger, you have access to the INSERTED logical table that represents the row to be inserted, with the value of the new ID it in.
Yes, it is possible by trigger, but keep in mind that TRIGGER doesn't take any input and doesn't provide any output, so you only can collect your desired data by querying in the trigger, however to satisfy insertion into your Table2 and Table3
CREATE TRIGGER tr_YourDesiredTriggerName ON Table1
FOR INSERT
AS
BEGIN
-- Inserting data to Table2
INSERT INTO Table2( Serial, Name)
SELECT i.Serial, i.Name
FROM Table1 AS t1 INNER JOIN Inserted AS i ON t1.Serial = i.Serial
AND i.Serial NOT IN ( SELECT t2.Serial FROM Table2 AS t2 )
-- Inserting data to Table3
INSERT INTO Table3( Serial, OtherData) -- select from other table
SELECT i.Serial, OtherData
FROM OtherTable AS ot INNER JOIN Inserted AS i ON ot.Serial = i.Serial
AND i.Serial NOT IN ( SELECT t3.Serial FROM Table3 AS t3 )
END
If you don't have control over the source of the insert then use a trigger. If you do have control over the source of the inserts then you can modify your insert process to also add the secondary table rows.
Will you also have control over future inserts? What if another insert gets created for this table? If you use a trigger then the secondary inserts would also get handled automatically. If not then the second insert process could possibly leave out the secondary inserts. Maybe that would be good or bad depending on your application.

Triggers in SQL Server 2008

I have created triggers for INSERT and UPDATE separately. The trigger is going to insert a row in Schema2 when an insert is made in Schema1. Tables:
Schema1.Temp1
Schema2.Temp2
The trigger creation is successful.
But when I am inserting data in Temp1, it is giving me error for Temp2 -- duplicate key. Temp2 has constraints for two other tables. What can be causing this, and how can it be resolved?
When your trigger is called try to write on Table2 (as you've told).
Peraphs you haven't written INSERT query using a condition on existence of your Temp1 row in Temp2.
Your query must be of this type:
INSERT INTO Table2 (field list)
SELECT field list
FROM inserted
WHERE NOT EXISTS(SELECT 'key' in Table2 t2 where t2.id = inserted.field_of_key)
In this way you prevent duplicate key, so if you want to update your table2 in insert too, you can write an UPDATE stament when that key is already existing.
Tell me if it's ok

Sql Server trigger insert values from new row into another table

I have a site using the asp.net membership schema. I'd like to set up a trigger on the aspnet_users table that inserted the user_id and the user_name of the new row into another table.
How do I go about getting the values from the last insert?
I can select by the last date_created but that seems smelly. Is there a better way?
try this for sql server
CREATE TRIGGER yourNewTrigger ON yourSourcetable
FOR INSERT
AS
INSERT INTO yourDestinationTable
(col1, col2 , col3, user_id, user_name)
SELECT
'a' , default , null, user_id, user_name
FROM inserted
go
You use an insert trigger - inside the trigger, inserted row items will be exposed as a logical table INSERTED, which has the same column layout as the table the trigger is defined on.
Delete triggers have access to a similar logical table called DELETED.
Update triggers have access to both an INSERTED table that contains the updated values and a DELETED table that contains the values to be updated.
You can use OLDand NEW in the trigger to access those values which had changed in that trigger. Mysql Ref
In a SQL Server trigger you have available two psdeuotables called inserted and deleted. These contain the old and new values of the record.
So within the trigger (you can look up the create trigger parts easily) you would do something like this:
Insert table2 (user_id, user_name)
select user_id, user_name from inserted i
left join table2 t on i.user_id = t.userid
where t.user_id is null
When writing triggers remember they act once on the whole batch of information, they do not process row-by-row. So account for multiple row inserts in your code.
When you are in the context of a trigger you have access to the logical table INSERTED which contains all the rows that have just been inserted to the table. You can build your insert to the other table based on a select from Inserted.
Create
trigger `[dbo].[mytrigger]` on `[dbo].[Patients]` after update , insert as
begin
--Sql logic
print 'Hello world'
end