Update third table when data updated on sql view - sql-server

I have got thease three tables:
CREATE TABLE tblEmployee1
(
Id int Primary Key,
Name nvarchar(30),
Gender nvarchar(10),
DepartmentId int
)
CREATE TABLE tblDepartment1
(
DeptId int Primary Key,
DeptName nvarchar(20)
)
CREATE TABLE tblEmployee
(
Id int Primary Key,
Name nvarchar(30),
Gender nvarchar(10),
DepartmentId int,
DeptName nvarchar(20)
)
Two of them are joined by a view:
Create view ViewEmployeeDetails1
as
Select Id, Name, Gender,DepartmentId, DeptName
from tblEmployee1
join tblDepartment1
on tblEmployee1.DepartmentId = tblDepartment1.DeptId
I have created the trigger so that when the view is updated
with data trigger fires and update the third table (tblEmployee) with data in the view wihout duplicate record.
Trigger dosent seem to insert values on to the tblEmployee when the view is updated. Can you please help me with the trigger dont know where it's going
worng My trigger is below Thanks in advance.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[Update_Employee]
ON dbo.ViewEmployeeDetails1
INSTEAD OF UPDATE
AS
BEGIN
insert into dbo.tblEmployee
([Id], [Name], [Gender], [DepartmentId], [DeptName])
SELECT i.[Id],i.[Name], i.[Gender], i.[DepartmentId], i.[DeptName]
FROM dbo.ViewEmployeeDetails1 t
INNER JOIN i
ON t.Id = i.Id
WHERE i.Id IS NOT NULL
end

There is no DeptName in the table, so you would seem to want:
insert into dbo.tblEmployee ([Id], [Name], [Gender], [DepartmentId])
SELECT i.[Id], i.[Name], i.[Gender], i.[DepartmentId]
FROM inserted i
WHERE i.Id IS NOT NULL
end;

Related

How can I insert a newly generated IDENTITY value into a related table with other fields?

I have a data table that contains a name and a social security number. I want to insert the name into a table with an identity field, then insert the ssn with that new identity field value into another table.
Below are the tables:
CREATE TABLE [data_table]
(
[name] [varchar](50) NOT NULL,
[ssn] [varchar](9) NOT NULL,
)
CREATE TABLE [entity_key_table]
(
[entity_key] [int] IDENTITY(1000000,1) NOT NULL,
[name] [varchar](50) NOT NULL,
)
CREATE TABLE [entity_identifier_table]
(
[entity_identifier_key] [int] IDENTITY(1000000,1) NOT NULL,
[entity_key] [int] NOT NULL,
[ssn] [int] NOT NULL,
)
This query works but doesn't link entity_key in [entity_key_table] TO ssn in [entity_identifier_table]:
INSERT INTO entity_key_table (name)
OUTPUT [INSERTED].[entity_key]
INTO [entity_identifier_table] (entity_key)
SELECT [name]
FROM [data_table]
This is what I want to do, but it doesn't work.
INSERT INTO entity (name)
OUTPUT [INSERTED].[entity_key], [data_table].[ssn]
INTO [entity_identifier] (entity_key,ssn)
SELECT [name]
FROM [data_table]
Rewriting my answer based on your requirements and the articles you linked. I think you can get that behavior doing something like this. I admit, I have never seen a merge on something like 1 != 1 like the article suggests, so I would be very cautious with this and test the bajeezes out out of it.
FWIW, it looks like during an INSERT, you can't access data that's not in the inserted virtual table, but updates (and apparently MERGE statements) can.
if object_id('tempdb.dbo.#data_table') is not null drop table #data_table
create table #data_table
(
[name] [varchar](50) NOT NULL,
[ssn] [varchar](9) NOT NULL,
)
if object_id('tempdb.dbo.#entity_key_table') is not null drop table #entity_key_table
create table #entity_key_table
(
[entity_key] [int] IDENTITY(1000000,1) NOT NULL,
name varchar(50)
)
if object_id('tempdb.dbo.#entity_identifier_table') is not null drop table #entity_identifier_table
create table #entity_identifier_table
(
[entity_identifier_key] [int] IDENTITY(2000000,1) NOT NULL,
[entity_key] [int] NOT NULL,
[ssn] varchar(9) NOT NULL,
)
insert into #Data_table (Name, SSN)
select 'John', '123456789' union all
select 'John', '001100110' union all
select 'Jill', '987654321'
merge into #entity_key_table t
using #data_table s
on 1 != 1
when not matched then insert
(
name
)
values
(
s.name
)
output inserted.entity_key, s.ssn
into #entity_identifier_table
(
entity_key,
ssn
);
select top 1000 *
from #data_table
select top 1000 *
from #entity_key_table
select top 1000 *
from #entity_identifier_table
The problem with your code is that you output data only from inserted or deleted.
Assuming your name column only relates to one SSN, the following would work:
DECLARE #output TABLE (entity_key INT,ssn VARCHAR (11))
INSERT INTO entity (entity_key, name)
OUTPUT [INSERTED].[entity_key], [inserted].[name]
INTO #output
SELECT D.Entity_key, d.name
FROM datatable
INSERT INTO entity_identifier (entity_key, ssn)
Select o.entity_key, d.snn
from #output o
join datatable d on o.name = d.name
However, the problem of multiple duplicated names having different Social Security Numbers is extremely high. In this case, your current structure simply does not work because there is no way to know which identity belongs to which name record. (The Merge solution in another post may also have this problem, before you put that to production be sure to test the scenario of duplicated names. The chances of duplicated names in a set of records is extremely high in any reasonable large data set of names and this should be one of your unit test for any potential solution.)
Here is a potential workaround. First, insert the SSN as the name in the first insert, then return output as shown but join on the #output Name column to the SSN column. After doing the other insert, then update the name in the orginal table to the correct name again joining on the SSN data.
DECLARE #output TABLE (entity_key INT,ssn VARCHAR (11))
INSERT INTO entity (entity_key, name)
OUTPUT [INSERTED].[entity_key], [inserted].[ssn]
INTO #output
SELECT D.Entity_key, d.name
FROM datatable
INSERT INTO entity_identifier (entity_key, ssn)
Select o.entity_key, d.output
from #output o
update e
set name = d.name
FROM entity e
join #output o on e.entity_key = o.entity_key
join datatable d on o.name = d.ssn

Execution plan showing type conversion warning - ON Cond columns

Getting Type conversion in Expression ..... may affect "CardinalityEstimate" in query pan choice.
The both tables T1 and T2 having clustered indexes.
How to overcome of warning?
CREATE TABLE T1
(
KEY INT IDENTITY(1,1) NOT NULL,
CODE VARCHAR(50),
DESCRIPTION VARCHAR(100),
EXTERNAL_KEY VARCHAR(15),
FLAG BIT
)
INSERT INTO T1 VALUES(1,'ASS','DESC','NULL',0)
INSERT INTO T1 VALUES(1,'ASS1','DESC','45213',1)
INSERT INTO T1 VALUES(1,'ASS2','DESC','NULL',1)
INSERT INTO T1 VALUES(1,'ASS3','DESC','NULL',0)
INSERT INTO T1 VALUES(1,'ASS4','DESC','56321',1)
CREATE TABLE T2
(
KEY INT IDENTITY(1,1) NOT NULL,
CODE VARCHAR(50),
DESCRIPTION VARCHAR(100),
EXTERNAL_KEY NUMERIC(14,0)
)
INSERT INTO T2 VALUES(1,'DSA','DESC',51256)
INSERT INTO T2 VALUES(1,'DSA1','DESC',45213)
INSERT INTO T2 VALUES(1,'DSA2','DESC',51256)
INSERT INTO T2 VALUES(1,'DSA3','DESC',56321)
Actual Query:
SELECT T1.KEY,T1.FLAG,T2.KEY, FROM T2 INNER JOIN T1 ON
CAST(NULLIF(RTRIM(T1.EXTERNAL_KEY),'') AS NUMERIC(14,0)) = T2.EXTERNAL_KEY
WHERE T1.EXTERNAL_KEY IS NOT NULL
There are a few issues with your Sample data as well as the query. KEY INT IDENTITY(1,1) NOT NULL should be changed to [KEY] NOT NULL Or Do not insert into the Identity column INSERT INTO T1 VALUES('ASS','DESC','NULL',0) Also you are not inserting DB NULL but Text null when you enclosed 'NULL' with single quotes. for this. The below code should work
SELECT T1.[KEY],T1.FLAG,T2.[KEY]
FROM T2
INNER JOIN T1
ON NULLIF(T1.EXTERNAL_KEY ,'NULL')=T2.EXTERNAL_KEY
WHERE T1.EXTERNAL_KEY IS NOT NULL
However I think you want NULL to be NULL and Not text so please change your insert from INSERT INTO T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG) VALUES('ASS','DESC','NULL',0) to `INSERT INTO T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG) VALUES('ASS','DESC',NULL,0)
Sample Data
IF OBJECT_ID('tempdb..#T1') IS NOT NULL
DROP TABLE #T1
IF OBJECT_ID('tempdb..#T2') IS NOT NULL
DROP TABLE #T2
CREATE TABLE #T1
(
[KEY] INT IDENTITY(1,1) NOT NULL,
CODE VARCHAR(50),
DESCRIPTION VARCHAR(100),
EXTERNAL_KEY VARCHAR(15),
FLAG BIT
)
GO
INSERT INTO #T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG) VALUES('ASS','DESC',NULL,0)
INSERT INTO #T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG) VALUES('ASS1','DESC','45213',1)
INSERT INTO #T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG)VALUES('ASS2','DESC',NULL,1)
INSERT INTO #T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG)VALUES('ASS3','DESC',NULL,0)
INSERT INTO #T1(CODE,DESCRIPTION,EXTERNAL_KEY,FLAG)VALUES('ASS4','DESC','56321',1)
CREATE TABLE #T2
(
[KEY] INT IDENTITY(1,1) NOT NULL,
CODE VARCHAR(50),
DESCRIPTION VARCHAR(100),
EXTERNAL_KEY NUMERIC(14,0)
)
GO
INSERT INTO #T2 (CODE,DESCRIPTION,EXTERNAL_KEY) VALUES('DSA','DESC',51256)
INSERT INTO #T2 (CODE,DESCRIPTION,EXTERNAL_KEY)VALUES('DSA1','DESC',45213)
INSERT INTO #T2 (CODE,DESCRIPTION,EXTERNAL_KEY)VALUES('DSA2','DESC',51256)
INSERT INTO #T2 (CODE,DESCRIPTION,EXTERNAL_KEY)VALUES('DSA3','DESC',56321)
Code
SELECT #T1.[KEY],#T1.FLAG,#T2.[KEY]
FROM #T2
INNER JOIN #T1
ON NULLIF(#T1.EXTERNAL_KEY ,NULL)=#T2.EXTERNAL_KEY
WHERE #T1.EXTERNAL_KEY IS NOT NULL

TRIGGER AFTER INSERT SELECT MIN(COUNT) insert ID

I'm trying to create a trigger after an insert on the eventss table. The trigger should select the Bcoordinator_ID from the bookingCoordinator table where they have the minimum number of occurrences in the eventss table.
Here's my table data followed by the trigger. It doesn't like the minCount in the values, I think it's looking for and int.
DROP TABLE eventsBooking
CREATE TABLE eventsBooking
(
EBK INT NOT NULL IDENTITY(100, 1),
booking_ID AS 'EBK'+CAST( ebk as varchar(10)) PERSISTED PRIMARY KEY,
bookingDate DATE,
Bcoordinator_ID VARCHAR (20),
eventss_ID VARCHAR (20) NOT NULL
)
INSERT INTO eventsBooking
VALUES ('2015-01-07 11:23:00', NULL, 'EVT100');
Eventss table:
EVT INT NOT NULL IDENTITY(100, 1),
eventss_ID AS 'EVT' + CAST(evt as varchar(10)) PERSISTED PRIMARY KEY,
eventsName varchar(50),
noOfStages SMALLINT,
noOfRounds SMALLINT,
eventsDate DATE,
entryFee DECIMAL (7,2),
venue_ID VARCHAR (20) NOT NULL,
judges_ID VARCHAR (20)
INSERT INTO eventss
VALUES ('Swimming Gala 2015', '3', '7', '2015-01-07 09:00:00', '35.00', 'VEN101', 'JUD100');
CREATE TABLE bookingCoordinator
(
BCO INT NOT NULL IDENTITY(100, 1),
Bcoordinator_ID AS 'BCO'+CAST( bco as varchar(10)) PERSISTED PRIMARY KEY,
forename varchar(20) NOT NULL,
familyName varchar(50)
)
INSERT INTO bookingCoordinator VALUES ('Steve', 'Wills');
Trigger:
CREATE TRIGGER TRGinsertJudge
ON [dbo].[eventss]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.eventsBooking (Bcoordinator_ID, bookingDate, Eventss_ID)
VALUES(minCount, getdate(), 100)
SELECT MIN(COUNT(Bcoordinator_ID)) AS minCount
FROM eventsBooking
END
You can't do an aggregation of an aggregation i.e. MIN(COUNT(1))
If you just want the Bcoordinatior_ID with the least counts in eventsBooking, do this
select top 1 bcoordinator_id
from eventsBooking
group by bcoordinator_id
order by count(1) asc
And you don't use VALUES() in an INSERT INTO ... SELECT statement
Also, in your current code, since eventsBooking.bcoordinator_id is always null, you need to join to the actual table of bookingCoordinators to return booking coordinators without any events booked.
So your complete trigger statement should be
INSERT INTO dbo.eventsBooking (Bcoordinator_ID, bookingDate, Eventss_ID)
select
top 1
bookingcoordinator.bcoordinator_id, getdate(), 100
from bookingCoordinator left join eventsBooking
on bookingCoordinator.Bcoordinator_ID = eventsBooking.Bcoordinator_ID
group by bookingcoordinator.bcoordinator_id
order by count(1) asc

SQL Server Trigger functioning backwards

I have a little strange issue with my SQL script and I was hoping someone could help me out with it.
I have a Database being created by using
IF EXISTS (SELECT name
FROM sysdatabases
WHERE name = 'travel')
DROP DATABASE travel
GO
CREATE DATABASE travel
GO
USE travel
GO
I then create 3 tables as shown below
CREATE TABLE customer
(
customerID INT,
lastname VARCHAR(70) NOT NULL,
firstname VARCHAR(70) NOT NULL,
phone VARCHAR(10) CONSTRAINT phoneCheck CHECK ((phone LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')),
category VARCHAR(7) NOT NULL CONSTRAINT categoryDefault DEFAULT 'A',
CONSTRAINT categoryCheck CHECK (category IN ('A', 'B', 'C')),
CONSTRAINT customerPK
PRIMARY KEY (customerID)
)
CREATE TABLE package /*Still need to do the Zero Padding*/
(
packageCode VARCHAR(6),
destination VARCHAR(70),
CONSTRAINT packageCodeCheck CHECK (packageCode LIKE ('YFK%')),
price MONEY NOT NULL CONSTRAINT priceCheck CHECK ((price BETWEEN 1000 AND 10000)),
passportRequired VARCHAR(7) NOT NULL CONSTRAINT passportRequiredDefault DEFAULT 'Y',
CONSTRAINT passportCheck CHECK (passportRequired IN ('Y', 'N')),
CONSTRAINT packagePK
PRIMARY KEY (packageCode)
)
CREATE TABLE booking /*Still need to do the Customer and Package delete*/
(
customerID VARCHAR(6),
bookingDate VARCHAR(70) NOT NULL DEFAULT GETDATE(),
amountPaid MONEY CONSTRAINT amountPaidDefault DEFAULT 0.00,
CONSTRAINT bookingPK
PRIMARY KEY (customerID)
)
Now heres the issue, I create a trigger as shown below
GO
CREATE TRIGGER customerDelete ON customer AFTER DELETE
AS
DELETE booking
FROM customer
WHERE customer.customerID = booking.customerID
GO
Which to my understanding it will delete all records in booking... that have the matching customerID WHEN a record is deleted from the customer Table. (I am new to triggers)
I INSERT Sample Data as shown below
INSERT INTO customer
(customerID, lastname, firstname, phone, category)
VALUES
(1, 'Picard', 'Corey', 1234567890, 'A'),
(2, 'Bond', 'Devon', 9876543210, 'B'),
(3, 'Douglas', 'Bryan', 6549871230, 'C')
INSERT INTO package
(packageCode, destination, price, passportRequired)
VALUES
('YFK001', 'Toronto', 1000.57, 'N'),
('YFK002', 'Orlando', 3000.98, 'Y')
INSERT INTO booking
(customerID, bookingDate, amountPaid)
VALUES
(1, GETDATE(), 1548),
(2, GETDATE(), 1586),
(3, GETDATE(), 1350),
(4, GETDATE(), 1650)
And Finally I delete the Customer from the TABLE customer with the customerID of 1 by using
DELETE customer
WHERE customerID = 1
However, when I attempt to see the results by using
SELECT * FROM customer
--WHERE customerID = 1 OR customerID = 2 OR customerID = 3
SELECT * FROM package
--WHERE packageCode = 'YFK001' OR packageCode = 'YFK002'
SELECT * FROM booking
--WHERE customerID = 1 OR customerID = 2 OR customerID = 3 OR customerID = 4
It displays bookings with customerID 1 and 4.
Can you let me know what I'm doing wrong?
The trigger is essentially used for the purpose of deleting the bookings with the same customerID of the customer we delete from the customer TABLE
All help is greatly appreciated.
Thanks,
Bryan
Change your delete to this:
DELETE B
FROM booking B
INNER JOIN DELETED D
ON B.customerID = D.customerID;
My Answer is not trigger, [ If you specially want to use Trigger then You can use Lamak's & Pradeep's Answer]
Here best way you can do is use Cacade on foreign Key in your case
Here is your Query I just updated cascade in it
CREATE TABLE customer
(
customerID INT,
lastname VARCHAR(70) NOT NULL,
firstname VARCHAR(70) NOT NULL,
phone VARCHAR(10) CONSTRAINT phoneCheck CHECK ((phone LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')),
category VARCHAR(7) NOT NULL CONSTRAINT categoryDefault DEFAULT 'A',
CONSTRAINT categoryCheck CHECK (category IN ('A', 'B', 'C')),
CONSTRAINT customerPK
PRIMARY KEY (customerID)
)
CREATE TABLE package /*Still need to do the Zero Padding*/
(
packageCode VARCHAR(6),
destination VARCHAR(70),
CONSTRAINT packageCodeCheck CHECK (packageCode LIKE ('YFK%')),
price MONEY NOT NULL CONSTRAINT priceCheck CHECK ((price BETWEEN 1000 AND 10000)),
passportRequired VARCHAR(7) NOT NULL CONSTRAINT passportRequiredDefault DEFAULT 'Y',
CONSTRAINT passportCheck CHECK (passportRequired IN ('Y', 'N')),
CONSTRAINT packagePK
PRIMARY KEY (packageCode)
ON DELETE CASCADE
ON UPDATE CASCADE
)
CREATE TABLE booking /*Still need to do the Customer and Package delete*/
(
customerID VARCHAR(6),
bookingDate VARCHAR(70) NOT NULL DEFAULT GETDATE(),
amountPaid MONEY CONSTRAINT amountPaidDefault DEFAULT 0.00,
CONSTRAINT bookingPK
PRIMARY KEY (customerID)
ON DELETE CASCADE
ON UPDATE CASCADE
)
This way when you are deleting or update [keyvalue] any entry of Table holding primary key will update its related foreign childs

Update timestamp column when Foreign table updates without Trigger

I'm trying to setup a Timestamp/Rowversion on a Parent table so that when the Child table updates, the Timestamp on the Parent table row changes.
I don't need to know about the row in the Child table, just that according to the parent this particular row has changed.
USE [Test]
GO
CREATE TABLE [dbo].[Clerk](
[ID] [int] NOT NULL,
[Name] [varchar](20) NOT NULL,
[lastUpdate] [timestamp] NOT NULL,
CONSTRAINT [PK_Clerk] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
CREATE TABLE [dbo].[ClerkAddress](
[ClerkID] [int] NOT NULL,
[Address] [varchar](40) NOT NULL,
CONSTRAINT [PK_ClerkAddress] PRIMARY KEY CLUSTERED
(
[ClerkID] ASC
)
ALTER TABLE [dbo].[ClerkAddress] WITH CHECK ADD CONSTRAINT [FK_ClerkAddress_Clerk] FOREIGN KEY([ClerkID])
REFERENCES [dbo].[Clerk] ([ID])
ON UPDATE CASCADE
insert into Clerk (ID, Name) values (1, 'Test1')
insert into Clerk (id, Name) values (2, 'Test2')
insert into ClerkAddress (ClerkID, Address) values (1, 'address1')
insert into ClerkAddress (ClerkID, Address) values (2, 'address2')
using the following code examples.
update ClerkAddress set Address = NEWID() where ClerkID = 2
--no change to Clerk when address changes
select * from Clerk
select * from ClerkAddress
--Of course these update the lastUpdate in clerk
update Clerk set Name = 'test2' where ID = 2
update Clerk set Name = name
Is this even possible or do I need to make triggers for the updates? (update clerk set name = name where id = ClerkID)
You can make it appear as though the parent row is updated with a view.
You need to add a rowversion column to ClerkAddress, then
CREATE VIEW dbo.Clerk2
WITH SCHEMABINDING -- works for me on SQL Server 2012
AS
SELECT
C.ID, C.Name, ISNULL(MAX(CA.lastUpdate), C.lastUpdate) AS lastupdate
FROM
[dbo].[Clerk] C
LEFT JOIN
[dbo].[ClerkAddress] CA ON C.ID = CA.ClerkID
GROUP BY
C.ID, C.Name, C.lastUpdate
GO
SELECT * FROM dbo.Clerk2;
GO
update ClerkAddress set Address = NEWID() where ClerkID = 2;
GO
SELECT * FROM dbo.Clerk2;
GO
update ClerkAddress set Address = NEWID() where ClerkID = 2;
GO
SELECT * FROM dbo.Clerk2;
GO
This uses the highest value from rowversion yet preserves the actual rowversion on Clerk (which can still be used for optimistic concurrency by the client)
This works because rowversion is database unique

Resources