Pre-Populate foreign keys in SQL with a new row - sql

I am trying to write a SQL script which will go through a table with a newly created FK and pre-populate the key with a new row in the foreign table. I'm not 100% on how to do this or if its even possible in a single statement but here's my attempt:
UPDATE [dbo].[Blogs]
set AuthorSecurableId = inserted.Id
FROM [dbo].[Blogs] updating
INNER JOIN
(INSERT INTO [dbo].[Securables] (Name)
OUTPUT Inserted.id, ROW_NUMBER() OVER (ORDER BY Inserted.Id) as rownum
SELECT 'Admin : ' + Name
FROM Blogs
WHERE AuthorSecurableId is null) inserted ON ROW_NUMBER() OVER (ORDER BY updating.Id) = inserted.rownum
WHERE updating.AuthorSecurableId is null
When I do this I get the following error
Msg 10727, Level 15, State 1, Line 5
A nested INSERT, UPDATE, DELETE, or MERGE statement is not allowed on either side of a JOIN or APPLY operator.
Below is a simple view of the schema I have
I would like to create a securable for each blog which doesn't have one and populate that blogs AuthorSecurableId with the ID of the newly created securable
I think I could do this with a cursor but I was wondering if there is a better single statement approach

You can use merge against Securables with output to a table variable or temp table and a separate update against Blogs.
declare #T table
(
SecID int,
BlogID int
);
merge Securables T
using (
select 'Admin : '+B.Name as Name, B.Id
from Blogs as B
where B.AuthorSecurabledId is null
) as S
on 0 = 1
when not matched by target then
insert (Name) values (S.Name)
output inserted.Id, S.Id
into #T;
update Blogs
set AuthorSecurabledId = T.SecID
from #T as T
where Blogs.Id = t.BlogID;
SQL Fiddle

Related

IF ELSE condition with SQL Server trigger

I am populating a table entirely using triggers, it populates the table if LocationID and ProductID does not exists and if it already does it updates the given data.
I have posted the following code snippet looking for a possible solution or link to one.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[newpurchase]
ON [dbo].[PurchaseMaster]
AFTER INSERT
AS
BEGIN
IF (((SELECT COUNT(*) FROM StockMaster
WHERE LocationID = (SELECT LocationID FROM inserted)) > 0)
AND ((SELECT COUNT(*) FROM StockMaster
WHERE LocationID = (SELECT LocationID FROM inserted)) > 0))
UPDATE StockMaster
SET TotalPurchased = TotalPurchased + (SELECT PurchasedQTY FROM inserted)
WHERE LocationID = (SELECT LocationID FROM inserted)
AND ProductID = (SELECT ProductID FROM inserted);
ELSE
INSERT INTO StockMaster (LocationID, ProductID, TotalPurchased, TotalSold, OnHand)
SELECT LocationID, ProductID, PurchasedQTY, 0, 0 FROM inserted;
END
I am not entirely sure exactly what you are looking for. If your return sets cannot be collected dynamically, and you need another table to always update based off the information persisted by another another table then I do think triggers are fine. There is always overhead with triggers, so keep that in mind.
You could do the way you are doing it, and from what I can see on the surface level the trigger may be what you are looking for (but this also depends upon your business needs and relationships of tables). HOWEVER, you will have to be careful if your Insert is a bulk insert then what you have will throw an error.
I also noticed your If statement is checking LocationId twice instead of LocationId and ProductId.
The trigger you are trying to create works with only one value at a time. You could rewrite this to a more set based trigger for when bulk inserts occur. This way if you have one insert or bulk inserts these two queries can perform for that values where needed.
first you want to update all the values where LocationId and ProductId are the >same found in StockMaster
if no values match then nothing will be updated
Update StockMaster
Set TotalPurchased = sm.TotalPurchased + i.PurchasedQty
From StockMaster sm
inner join inserted i on sm.LocationId = i.LocationId and sm.ProductId = i.ProductId
next you want to insert any row that isn't found in StockMaster
if all values matched above, they will be weeded out with the wheree condition >and,then nothing would be inserted here, those that didn't match would be
inserted
INSERT INTO StockMaster (LocationID, ProductID, TotalPurchased, TotalSold, OnHand)
SELECT i.LocationID, i.ProductID, i.PurchasedQTY, 0, 0
FROM inserted i
left join StockMaster sm on i.LocationId = sm.LocationId and i.ProductId = sm.ProductId
where sm.{Id} is null;--not {id} is for whatever key this table uses

Make a trigger to only trigger if a certain column is updated

I'm trying to make a trigger to only trigger if a certain column is updated, and then only if the column is updated to 'Executed'. I can update if the column is changed, but I can't seem to find a way to update if column is updated to 'Executed'
CREATE TRIGGER dbo.NewTrigger
ON dbo.Database
AFTER UPDATE
AS
IF Update(Status) = 'Executed'
BEGIN
--MY insert into statement. This adds data to another table, but I only want the whole process to run if the original table column "Status" is set to "Executed"
END
Could someone assist please?
You'd need to use the inserted and deleted tables in the trigger, see here:
Use Inserted and Deleted Tables
In case of an update:
inserted table: contains new column values of rows that have been updated
deleted table: contains old column values of rows that have been updated
Your trigger could look something like this:
create table t (id int identity, status varchar(100));
create table audit(id int, old_status varchar(100), new_status varchar(100), updated_at datetime);
create trigger StatusUpdate
on t
After UPDATE as
if (update(status)
and exists(select * from inserted i
inner join deleted d on d.id = i.id
where d.status != 'Executed'
and i.status = 'Executed'))
begin
insert into audit (id, old_status, new_status, updated_at)
select i.id, d.status, i.status, getdate()
from inserted i
inner join deleted d on d.id = i.id
where d.status != 'Executed'
and i.status = 'Executed'
end
See Demo on DB Fiddle
Demo 2 - Multiple rows updated together

INSTEAD OF INSERT trigger & Identity column using view

I'm building a database that will store client data for a company. The tables in the DB are normalized, so I have multiple tables that are linked together using Foreign Key Constraints.
Microsoft Access will be used to interface with the database (as a frontend). To make things simpler, I created a view that joins all the required tables together so that end-users can query information without hassle.
The problem I've run into involves INSERTING information into this view. From my understanding, since I have multiple tables in my view, I have to use a trigger with an INSTEAD OF INSERT statement. I've created the trigger; however, I'm unsure how to work with the ID columns in the tables (these act as keys).
I have a MemberBasicInformation table that contains the client's DOB, Name, Gender, etc AND their MemberID. This ID is an IDENTITY column in the table, so it is generated automatically. The problem that I'm running into is that because the identity is automatically generated, I'm unable to grab the identity value that is generated after the insert into the MemberBasicInformation table and insert it into the other related tables. When I attempt to do so, I end up with a Foreign Key constraint violations.
I've tried using ##Identity and Scope_Identity() to no avail. I've listed my view and trigger to give you an idea of how things are set up. I would greatly appreciate if someone could point me in the right direction. I'm truly at a loss.
MEMBER view:
CREATE VIEW [dbo].[Member]
AS
SELECT
dbo.MemberBasic.MemberId, dbo.MemberBasic.FirstName,
dbo.MemberBasic.MiddleInitial, dbo.MemberBasic.LastName,
dbo.MemberBasic.FullName, dbo.MemberBasic.DateOfBirth,
dbo.Gender.Name AS Gender, dbo.MemberBasic.Address,
dbo.MemberBasic.Address2, dbo.MemberBasic.City,
dbo.MemberBasic.State, dbo.MemberBasic.ZipCode,
dbo.MemberBasic.PhoneNumber,
dbo.MemberBasic.SocialSecurityNumber,
dbo.MemberBasic.DriversLicense,
dbo.MemberBasic.EmployerIdentificationNumber,
dbo.MemberBasic.Notes,
dbo.FieldRep.Name AS FieldRepName,
dbo.MemberDetail.DateAssigned AS FieldRepDateAssigned,
dbo.MemberDetail.CPReceivedOn, dbo.MemberDetail.CredentialedOn,
dbo.MemberEligibility.IsActive, dbo.ICO.Name AS ICO,
dbo.MemberEligibility.StartDate AS EligibilityStartDate,
dbo.MemberEligibility.EndDate AS EligibilityEndDate,
dbo.MemberWorkerCompDetail.ExpirationDate AS WorkerCompExpirationDate,
dbo.MemberWorkerCompDetail.AuditDate AS WorkerCompAuditDate,
dbo.WorkerCompTier.Name AS WorkerCompTier,
dbo.MemberAttachment.AttachmentId,
dbo.MemberAttachment.Data AS AttachmentData
FROM
dbo.MemberAttachment
INNER JOIN
dbo.MemberBasic ON dbo.MemberAttachment.MemberId = dbo.MemberBasic.MemberId
INNER JOIN
dbo.MemberCaregiverAssignment ON dbo.MemberAttachment.MemberId = dbo.MemberCaregiverAssignment.MemberId
INNER JOIN
dbo.MemberDetail ON dbo.MemberBasic.MemberId = dbo.MemberDetail.MemberId
INNER JOIN
dbo.MemberEligibility ON dbo.MemberAttachment.MemberId = dbo.MemberEligibility.MemberId
INNER JOIN
dbo.MemberWorkerCompDetail ON dbo.MemberAttachment.MemberId = dbo.MemberWorkerCompDetail.MemberId
INNER JOIN
dbo.Gender ON dbo.MemberBasic.GenderId = dbo.Gender.GenderId
INNER JOIN
dbo.FieldRep ON dbo.MemberDetail.FieldRepId = dbo.FieldRep.FieldRepId
INNER JOIN
dbo.ICO ON dbo.MemberEligibility.ICOId = dbo.ICO.ICOId
INNER JOIN
dbo.WorkerCompTier ON dbo.MemberWorkerCompDetail.TierId = dbo.WorkerCompTier.TierId
GO
MEMBER trigger:
ALTER TRIGGER [dbo].[InsertNewMember]
ON [dbo].[Member]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO MemberBasic (FirstName, MiddleInitial, LastName, GenderId, DateOfBirth, Address, Address2, City, State, ZipCode, PhoneNumber, SocialSecurityNumber, DriversLicense, EmployerIdentificationNumber, Notes)
SELECT
FirstName, MiddleInitial, LastName, GenderId, DateOfBirth,
Address, Address2, City, State, ZipCode, PhoneNumber,
SocialSecurityNumber, DriversLicense,
EmployerIdentificationNumber, Notes
FROM
inserted
INNER JOIN
Gender ON Gender.Name = Gender;
INSERT INTO MemberDetail (MemberId, FieldRepId, DateAssigned, CPReceivedOn, CredentialedOn)
SELECT
MemberId, FieldRep.FieldRepId, FieldRepDateAssigned,
CPReceivedOn, CredentialedOn
FROM
inserted
INNER JOIN
FieldRep ON FieldRep.Name = FieldRepName;
INSERT INTO MemberEligibility (MemberId, ICOId, StartDate, EndDate)
SELECT
MemberId, ICOId, EligibilityStartDate, EligibilityEndDate
FROM
inserted
INNER JOIN
ICO ON ICO.Name = ICO;
INSERT INTO MemberWorkerCompDetail (MemberId, AuditDate, ExpirationDate, TierId)
SELECT
MemberId, WorkerCompAuditDate, WorkerCompExpirationDate, TierId
FROM
inserted
INNER JOIN
WorkerCompTier ON WorkerCompTier.Name = WorkerCompTier;
INSERT INTO MemberAttachment (MemberId, Data)
SELECT MemberId, AttachmentData
FROM Member
END
You can do this by using a table variable to hold the newly inserted IDs and all of the data that you're going to insert into the other tables. You need to do this because there's no way from just the first INSERTs data to join back to inserted in such a manner that you can match up the IDENTITY values with the rows that caused them to be generated.
We also have to abuse MERGE since INSERT doesn't let you include anything other than the target table in its OUTPUT clause.
I'm doing this on a toy example but hopefully you can see how to write it for your full table structures.
First, some tables:
create table dbo.Core (
ID int IDENTITY(-71,3) not null,
A varchar(10) not null,
constraint PK_Core PRIMARY KEY (ID)
)
go
create table dbo.Child1 (
ID int IDENTITY(-42,19) not null,
ParentID int not null,
B varchar(10) not null,
constraint PK_Child1 PRIMARY KEY (ID),
constraint FK_Child1_Core FOREIGN KEY (ParentID) references Core(ID)
)
go
create table dbo.Child2 (
ID int IDENTITY(-42,19) not null,
ParentID int not null,
C varchar(10) not null,
constraint PK_Child2 PRIMARY KEY (ID),
constraint FK_Child2_Core FOREIGN KEY (ParentID) references Core(ID)
)
go
And the view:
create view dbo.Together
with schemabinding
as
select
c.ID,
c.A,
c1.B,
c2.C
from
dbo.Core c
inner join
dbo.Child1 c1
on
c.ID = c1.ParentID
inner join
dbo.Child2 c2
on
c.ID = c2.ParentID
go
And finally the trigger:
create trigger Together_T_I
on dbo.Together
instead of insert
as
set nocount on
declare #tmp table (ID int not null, B varchar(10) not null, C varchar(10) not null);
merge into dbo.Core c
using inserted i
on
c.ID = i.ID
when not matched then insert (A) values (i.A)
output
inserted.ID /* NB - This is the output clauses inserted,
not the trigger's inserted so this is now populated */
,i.B,
i.C
into #tmp;
insert into dbo.Child1(ParentID,B)
select ID,B
from #tmp
insert into dbo.Child2(ParentID,C)
select ID,C
from #tmp
(And I would keep something like that comment in there since statements inside triggers that include OUTPUT clauses tend to be quite confusing since there are two inserted tables in play)
(It's also noteworthy that it doesn't really matter what you put in the ON clause of the MERGE, so long as you're sure that it will fail to make a match. You may prefer to have, say, just 1=0 if you think it makes it clearer. I just went cute on the fact that the trigger's inserted.ID will be NULL)
And now we do our insert:
insert into dbo.Together(A,B,C) values ('aaa','bbb','ccc')
go
select * from dbo.Together
And get the result:
ID A B C
----------- ---------- ---------- ----------
-71 aaa bbb ccc
Here is answer to your question "Getting Identity values to use as FK in an INSTEAD OF trigger"
https://dba.stackexchange.com/questions/34258/getting-identity-values-to-use-as-fk-in-an-instead-of-trigger

SQL Server 2005 Bulk insert on split string where not exists

I have a problem with an insert statement where I cannot get the "where not exists" clause to work. I would prefer a solution where I do not have to dump my list into a declared a table. Here are two of the solutions I have tried, could someone please tell where my syntax is wrong? My split string list has three values, the first two values would be new, but the third value already exists in the database and so should not be inserted.
With this sample I get no data returned at all:
insert into dbo.Cook(Id, CookId, DateEntered, Active)
select
#Id, value as CookId, getDate(), 1
from
dbo.Split('123456,234567,345678', ',')
left join
dbo.Cook ck on ck.Id = #Id
where
not exists (select CookId from dbo.Cook where Id = #Id)
Then I tried a version with a table which winds up returning all the values in my #ids table including the cook number that would be a duplicate:
declare #ids table (id int)
insert into #ids
select value as id
from dbo.Split('123456,234567,345678', ',')
insert into dbo.Cook(Id, CookId, DateEntered, Active)
select
#Id, id as CookId, getDate(), 1
from
#ids
left outer join
dbo.Cook ck on ck.CookId = id
where
not exists (select CookId from dbo.Cook where ck.CookId != id)
I have the solution to my problem, thanks goes out to a fellow programmer. My problem was I should have being doing the join on the select statement and adding a comparison column to get the information I was looking for. The solution was this:
insert into dbo.Cook(Id, CookId, DateEntered, Active)
select
#Id, value as CookId, getDate(), 1
from
dbo.Split('123456,234567,345678', ',') c
left join
(select cook from dbo.Cook where Id = #Id) cook on cook.Id = c.value
where
cook.Id is null
Aside from your neglecting to put your from clause on a new line cringe, I believe it is your syntax.
I don't understand what you are trying to do here. dbo.Split is not a built in function in SQL Server as far as I know.

sql server insert data from table1 to table2 where table1=table3 data

I am trying to insert into Table A, unique data from Table B that matches data in TAble C but I am keep getting the violation of primary key error and not sure what I am doing wrong
Table A - bookfeed
Table B - bookF
Table C - bookStats
INSERT INTO bookFeed
(entryId,feed,entryContent,pubDate,authorName,authorId,age,
sex,locale,pic,fanPage, faceTitle,feedtype,searchterm,clientId,dateadded)
SELECT DISTINCT b.entryId,b.feed,b.entryContent,b.pubDate,b.authorName,
b.authorId,b.age,b.sex,b.locale,b.pic,b.fanPage,b.faceTitle,b.feedtype,
b.searchterm, b.clientId,b.dateadded
FROM bookF as b
INNER JOIN bookStats as a on a.feed = b.feed
WHERE NOT EXISTS (SELECT *
FROM bookFeed as c
WHERE c.entryId = b.entryId)
Table A bookFeed has a primary key on entryId
It looks like in table bookF, there are duplicate records per entryId
If you only want one entryId (limited by PK on bookFeed), you can use this. Adjust the order by in the ROW_NUMBER to suit
insert into bookFeed (
entryId,feed,entryContent,pubDate,authorName,authorId,age,
sex,locale,pic,fanPage,faceTitle,feedtype,searchterm,clientId,dateadded)
select
entryId,feed,entryContent,pubDate,authorName,authorId,age,
sex,locale,pic,fanPage,faceTitle,feedtype,searchterm, clientId,dateadded
from
(
select rn=Row_number() over (partition by b.entryId order by b.entryId),
b.entryId,b.feed,b.entryContent,b.pubDate,b.authorName,b.authorId,b.age,
b.sex,b.locale,b.pic,b.fanPage,b.faceTitle,b.feedtype,b.searchterm, b.clientId,b.dateadded
from bookF as b
inner join bookStats as a on a.feed = b.feed
left join bookFeed as c on c.entryId=b.entryId
where c.entryId is null
) X
where rn=1
UPDATE : Try this for you query and see if it works,look at the data and see if there are duplicates meaning all the entries should have an entry id different than what is currently there -
SELECT b.entryId,b.feed,b.entryContent,b.pubDate,b.authorName,
b.authorId,b.age,b.sex,b.locale,b.pic,b.fanPage,b.faceTitle,b.feedtype,
b.searchterm, b.clientId,b.dateadded
FROM bookF as b
INNER JOIN bookStats as a on a.feed = b.feed
WHERE b.entryId IN (SELECT distinct entryid
FROM bookFeed)
I think you are trying to insert an entry id which is an primary key(check if the value trying to insert is not an duplicate,null or violates any other of primary key constraint)...so either dont try to insert it if it gets populated automatically or in case you are looking to insert it then turn on identity insert on..and try again...
But ideally your id should be calculated (auto increment or something) and never be inserted directly.