I need a function which will insert rows in multiple tables - database

postgresql
So i am inserting a row in a table1 and this row contains one field which is a field in table2. So i would like to create a function which will insert a row in table2 when i am inserting a row in table1.
So example:
I have two tables
table1
....
....
....
table2
....
....
....
I insert in table1
Insert in table1 values ("Sam","USA");
as a result i want to have
table1
Sam Usa
...
...
...
table2
Usa ...
...
...
So what function should i write and what trigger? Also if there is a row in table2 which has a field USA, this function should not insert one more row with USA
Sorry, if i explained it to complicated

Like #Jorge Campos mentioned in comment duplication of data is usually bad idea.
But if you are really have some scenario where you need to do this you need to create trigger on source table and insert data in destination table with existing check. Here is example in SQL Server:
CREATE TABLE Tbl1(
Id INT NOT NULL IDENTITY PRIMARY KEY,
Name NVARCHAR(100) NOT NULL
)
GO
CREATE TABLE Tbl2(
Id INT NOT NULL IDENTITY PRIMARY KEY,
Name NVARCHAR(100) NOT NULL
)
GO
CREATE TRIGGER Sync ON Tbl1 AFTER INSERT AS
INSERT INTO Tbl2 (Name)
SELECT src.Name FROM inserted src
LEFT JOIN Tbl2 dst ON src.Name = dst.Name
WHERE dst.Id IS NULL
GO
INSERT INTO Tbl1 (Name) VALUES ('STR')
SELECT * FROM Tbl2

Related

Copy IDENTITY column from another table and generate new IDs for missing records

I'm trying to copy identity column from one table to another, but I'm getting this error:
Cannot update identity column 'ID'.
I tried following code:
ALTER TABLE [dbo].[TableA]
ADD [ID] INT IDENTITY(20000,1), -- MAX(TableB.Id) < 20000
SET IDENTITY_INSERT TableA ON
UPDATE TableA
SET TableA.[ID] = TableB.[ID]
FROM TableA
INNER JOIN TableB ON TableB.ID = TableA.ID
SET IDENTITY_INSERT TableA OFF;
Scenario
I have two tables with 1:0-1 relationship.
TableA: Code (PK)
A,
B,
C,
D
TableB: Id (PK), Code (Unique)
1, A
2, B
3, D
Question
How to
Create new identity column TableA.Id
Copy values from TableB.Id
Ensure new unique values for TableA.Id if it was missing in TableB.Id
=NOT A REAL ANSWER TO THE ORIGINAL QUESTION, see comment=
You can not update a column which in itself is your join criteria. This just ain't work.
The safest way to keep the identity column intact is to delete rows from Table B in Table A first.
Then just insert from Table B into Table A with IDENTITY_INSERT ON.
Also this assumes there is a 1:1 relationship between the rows common to Table A and Table B. A 1:0-1 relationship between Table A and B means you need to capture ID from Table B in Table A as a foreign key relationship and separate column instead if this can lead to NULL results in Table A.
So in a nutshell (pseudo code) with assumption of 1:1 relationship between the rows:
DELETE FROM Table A WHERE Key EXISTS IN (SELECT Key FROM Table B)
;
SET IDENTITY_INSERT Table A ON
;
INSERT INTO Table A
( id_column, [other_columns [, othercolumns] ] )
SELECT
id_column, [other_columns [, othercolumns] ]
FROM Table B
;
SET IDENTITY_INSERT Table A OFF
;

How to insert rows in another table based on insert in first table

If any insert happens in table A then,i need to insert the last inserted row into table B.
How can I do it by using ##rowcount.
I am trying below code.
create table table1
(
id int identity(1,1),
column1 nvarchar
)
create table table2
(
id int identity(1,1),
column1 nvarchar
)
Create procedure insert1
#column1 nvarchar
AS
Declare #t int,#column2 nvarchar
insert into table1 values(#column1)
select * from table1
set #t= (Select ##IDENTITY from table1)
Insert into table2 values (#t)
Please let me know how can i do the same by trigger.
You could write a trigger something like this:
CREATE TRIGGER trgTableAInsert
ON dbo.Table1
FOR INSERT
AS
INSERT INTO dbo.Table2(Column1)
SELECT Column1
FROM Inserted
Points to note:
a trigger is called once per statement, e.g. if your INSERT statement inserts 10 rows, the trigger is called once and Inserted contains those 10 newly inserted rows (do you want to insert all 10 of those into TableB?)
I would recommend to always use the schema prefix on tables (the dbo. part)
I would recommend to always explicitly specify the list of columns, both on an INSERT as well as a SELECT statement - don't omit those! (or you might run into messy and hard-to-debug issues when suddenly one of the tables changes)
MERGE INTO Table1 AS t1
USING MyTable ON 1=0 -- always generates "not matched by target"
WHEN NOT MATCHED BY TARGET THEN
-- INSERT into Table1:
INSERT (A, B, C) VALUES (t1.A, t1.B, t1.C)
--- .. and INSERT into Table2:
OUTPUT inserted.ID, MyTable.D, MyTable.E, MyTable.F
INTO Table2 (ID, D, E, F);

Choosing when to insert data into Identity column

I am trying to do a bulk insert from a select statement, but sometimes I would like to save the Identity from Table2 to Table1, but sometimes I want the Identity in the Table to be generated automatically when there is no data in Table2, So how would this be accomplished?
INSERT INTO Table (ID,Name)
SELECT
CASE WHEN Col1 IS NOT NULL THEN Col1 ELSE ##identity END ID,
Col2 Name,
FROM Table2
Is this possible or do I have to do 2 seperate bulk import processes?
Yes you need two bulk inserts:
SET IDENTITY_INSERT Table ON
INSERT INTO Table (ID,Name)
SELECT
Col1
Col2 Name
FROM Table2
WHERE Col1 IS NOT NULL
SET IDENTITY_INSERT Table OFF
INSERT INTO Table (Name)
SELECT
Col2 Name
FROM Table2
WHERE Col1 IS NULL

How cascade Update/Delete works internally in SQL Server?

Ok, I believe the question was not clear. Here i rewrite this in other way.
Suppose i create two tables,
table1(c1 int PRIMARY KEY)
table2(table1c11 int)
There is a relation between table1 and table2
i.e. table1.c1=table2.table1c11
And, i execute the following statement in the table1 and table2
insert into table1(c1)
values('a'),('b'),('c'),('d'),('e')
insert into table2(table1c11)
values('a'),('a'),('b'),('d')
And now what I want to achieve is that, once I update the value of c1 in table1 the corresponding data in table2 gets changed automatically. For this I need to create the constraint in table1 and table2 relationships and apply the CASCADE UPDATE.
So, later I apply a new SQL update statement in table1 i.e.
Update table1 set c1=c1+'updated'
Then the data in table2 gets changed also, But what if I want to achieve the same functionality via INSTEAD OF UPDATE TRIGGER, then I need to write the instead of update trigger and inside that, I need to handle that with two magic tables INSERTED and DELETED.
But the main point is that, in this case, I have only one column present in the table1 and I am updating that same column, so how could i map the inserted and deleted rows. Same thing is being done by the SQL Server as well if I use CASCADing.
So, the question arises how SQL Server handles batch update in case of the primary key data changes in the table.
So, the question arises how SQL Server handles batch update in case of
the primary key data changes in the table.
SQL Server builds a query plan for the update statement that update both tables.
Create the tables:
create table T1
(
T1ID int primary key
);
create table T2
(
T2ID int primary key,
T1ID int references T1(T1ID) on update cascade
)
Add some data:
insert into T1 values(1), (2)
insert into T2 values(1, 1), (2, 1), (3, 2)
Update primary key of T1:
update T1
set T1.T1ID = 3
where T1.T1ID = 1
The query plan for the update looks like this:
The plan has two Clustered Index Update steps, one for T1 and one for T2.
Update 1:
How does SQL Server keep track of the rows to update when more than one primary key value is updated?
update T1
set T1.T1ID = T1.T1ID + 100
The Eager Spool in the top branch (update of T1) saves the old T1ID and the new calculated T1ID (Expr1013) to a temporary table that is used by the lower branch (update of T2). The Hash Match in the lower branch is joining the Table Spool with T2 on the old T1ID. Output from the Hash Match to the update of T2 is T2ID from the Clustered Index Scan of T2 and the new calculated T1ID (Expr1013) from the Table Spool.
Update 2:
If you need to replace the cascade update with a instead of trigger you need to have a way to join the inserted and deleted tables in the trigger. That can be done with a surrogate key in T1.
Tables:
create table T1
(
T1ID int primary key,
ID int identity unique
);
create table T2
(
T2ID int primary key,
T1ID int references T1(T1ID)
);
The trigger could look like this.
create trigger tr_T1 on T1 instead of update as
insert into T1(T1ID)
select T1ID
from inserted;
update T2
set T1ID = I.T1ID
from inserted as I
inner join deleted as D
on I.ID = D.ID
where D.T1ID = T2.T1ID;
delete from T1
where T1ID in (
select T1ID
from deleted
);
SQL Fiddle

Reordering Identity primary key in sql server

Yes i am very well aware the consequences. But i just want to reorder them. Start from 1 to end.
How do I go about reordering the keys using a single query ?
It is clustered primary key index
Reordering like
First record Id 1
second record Id 2
The primary key is Int
Drop PK constraint
Drop Identity column
Re-create Identity Column
Re-Create PK
USE Test
go
if(object_id('IdentityTest') Is not null)
drop table IdentityTest
create table IdentityTest
(
Id int identity not null,
Name varchar(5),
constraint pk primary key (Id)
)
set identity_insert dbo.IdentityTest ON
insert into dbo.IdentityTest (Id,Name) Values(23,'A'),(26,'B'),(34,'C'),(35,'D'),(40,'E')
set identity_insert dbo.IdentityTest OFF
select * from IdentityTest
------------------1. Drop PK constraint ------------------------------------
ALTER TABLE [dbo].[IdentityTest] DROP CONSTRAINT [pk]
GO
------------------2. Drop Identity column -----------------------------------
ALTER table dbo.IdentityTest
drop column Id
------------------3. Re-create Identity Column -----------------------------------
ALTER table dbo.IdentityTest
add Id int identity(1,1)
-------------------4. Re-Create PK-----------------------
ALTER TABLE [dbo].[IdentityTest] ADD CONSTRAINT [pk] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
--------------------------------------------------------------
insert into dbo.IdentityTest (Name) Values('F')
select * from IdentityTest
IDENTITY columns are not updatable irrespective of SET IDENTITY_INSERT options.
You could create a shadow table with the same definition as the original except for the IDENTITY property. Switch into that (this is a metadata only change with no movement of rows that just affects the table's definition) then update the rows and switch back though.
A full worked example going from a situation with gaps to no gaps is shown below (error handling and transactions are omitted below for brevity).
Demo Scenario
/*Your original table*/
CREATE TABLE YourTable
(
Id INT IDENTITY PRIMARY KEY,
OtherColumns CHAR(100) NULL
)
/*Some dummy data*/
INSERT INTO YourTable (OtherColumns) VALUES ('A'),('B'),('C')
/*Delete a row leaving a gap*/
DELETE FROM YourTable WHERE Id =2
/*Verify there is a gap*/
SELECT *
FROM YourTable
Remove Gaps
/*Create table with same definition as original but no `IDENTITY`*/
CREATE TABLE ShadowTable
(
Id INT PRIMARY KEY,
OtherColumns CHAR(100)
)
/*1st metadata switch*/
ALTER TABLE YourTable SWITCH TO ShadowTable;
/*Do the update*/
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY Id) AS RN
FROM ShadowTable
)
UPDATE CTE SET Id = RN
/*Metadata switch back to restore IDENTITY property*/
ALTER TABLE ShadowTable SWITCH TO YourTable;
/*Remove unneeded table*/
DROP TABLE ShadowTable;
/*No Gaps*/
SELECT *
FROM YourTable
I don't think there is any way to do this in a single query. Your best bet is to copy the data to a new table, drop and recreate the original table (or delete the data and reseed the identity) and reinsert the data in the original order using the previous identity as the ordering (but not re-inserting it).
CREATE TABLE Table1_Stg (bla bla bla)
INSERT INTO Table1_Stg (Column2, Column3,...) SELECT Column2, Column3,... FROM Table1 ORDER BY Id
Here the Id column is excluded from the SELECT column list.
Or, you can do:
SELECT * INTO Table1_Stg FROM Table1 ORDER BY Id
DROP Table1
sp_rename Table1_stg Table1
Please lookup the usage for sp_rename as I am doing this from memory.
Hope this helps.
EDIT: Please save a script with all your indexes and constraints if any on Table1.
EDIT2: Added second method of creating table and inserting into table.
UPDATE tbl SET id = (SELECT COUNT(*) FROM tbl t WHERE t.id <= tbl.id);
This last statement is genius. Just had to remove the primary key from the table design first and make sure under the design option Identity Specifications is set to no. Once you run the query set these options back.

Resources