INSERT or UPDATE table from another table with composite primary key - sql

I am looking for the correct syntax and way to do the following directly from SQL: insert or update (if data already exists inside) TableMain from data contained in TableA with both having same composite primary key.
Both tables are defined as :
CREATE TABLE TableA (
[TID0] [int] NOT NULL,
[TID1] [int] NOT NULL,
[language] [nvarchar](2) NOT NULL,
[TID2] [nvarchar](200) NOT NULL,
[text] [nvarchar](max) NULL,
[updatedOn] [datetime] NOT NULL DEFAULT (getdate())
PRIMARY KEY (
[TID0],
[TID1],
[language],
[TID2],
)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
TableA will be periodically deleted and filled.
TableMain as the same definition but will contain many more rows of data and what I need is to insert never seen values from TableA into TableMain, and update already existing rows.
I used to do this kind of insert but I do not know how to handle update and composite primary keys :
INSERT INTO TableMain
SELECT * FROM TableA
EDIT : i am using SQL Server 9.00.5000
EDIT : another way inspired by MERGE and mimick it
DECLARE #updatedIDs TABLE(
[TID0] [int],
[TID1] [int],
[language] [nvarchar](2),
[TID2] [nvarchar](200),
PRIMARY KEY ([TID0], [TID1], [language], [TID2]) -- as stated by Nikola Markovinović above, thanks
);
-- First update records
update TableMain
set [text] = source.[text],
[updatedOn] = source.[updatedOn]
OUTPUT
inserted.[TID0]
inserted.[TID1]
inserted.[language]
inserted.[TID2]
INTO #updatedIDs
from
TableMain AS main
, TableA AS source
WHERE
TableMain.[TID0] = source.[TID0]
and TableMain.[TID1] = source.[TID1]
and TableMain.[language] = source.[language]
and TableMain.[TID2] = source.[TID2]
-- And then insert
insert into TableMain
select *
from TableA AS source
where not exists
(
select 1
from #updatedIDs AS i
where i.[TID0] = source.[TID0]
and i.[TID1] = source.[TID1]
and i.[language] = source.[language]
and i.[TID2] = source.[TID2]
)

you should use a merge statment
something like this:
merge TableMain AS target
using TableA as source
ON <join tables here>
WHEN MATCHED THEN <update>
WHEN NOT MATCHED BY TARGET <Insert>
WHEN NOT MATCHED BY SOURCE <delete>

Here is a script you might use to upsert your data:
-- On error transaction is automatically rolled back
set xact_abort on
begin transaction
-- First update records
update TableMain
set [text] = source.[text],
[updatedOn] = source.[updatedOn]
from TableMain
inner join TableA source
on TableMain.[TID0] = source.[TID0]
and TableMain.[TID1] = source.[TID1]
and TableMain.[language] = source.[language]
and TableMain.[TID2] = source.[TID2]
-- And then insert
insert into TableMain ([TID0], [TID1], [language], [TID2], [text], [updatedOn])
select [TID0], [TID1], [language], [TID2], [text], [updatedOn]
from TableA source
where not exists
(
select *
from TableMain
where TableMain.[TID0] = source.[TID0]
and TableMain.[TID1] = source.[TID1]
and TableMain.[language] = source.[language]
and TableMain.[TID2] = source.[TID2]
)
commit transaction
You might rewrite not exists() as left join ... where TableMain.TID0 is null if performance is not satisfactory.

you can use the Merge command from SQLServer 2008. It allows you to merge data from another data source into your main data source and define specific behavior when there is a key match (and you'll probably like to update your table) or there is not match and you'll like to insert the new record.
http://blog.sqlauthority.com/2010/06/08/sql-server-merge-operations-insert-update-delete-in-single-execution/
you can visit this link to get few code samples.

Related

How to dynamically create columns in SQL based on a list of ids

I am developing a stored procedure in SQL that receives a list of Ids (INT) from the backend. I need to dynamically create columns based on these and give the columns for each ID a name. Below is an example of how to do this with a fixed number of known Ids
SELECT DISTINCT
SE.SE_optionCode AS OptionCode,
LTRIM(RTRIM(DO.DO_Description)) AS Description,
LTRIM(RTRIM(CASE WHEN DG.DG_Description IS NULL THEN '' ELSE DG.DG_Description END)) AS GenericDescription,
LTRIM(RTRIM(CASE WHEN DG.DG_GenericCode IS NULL THEN 0 ELSE DG.DG_GenericCode END)) as GenericCode,
DCS.DC_CatCode AS Category,
LTRIM(RTRIM(DCS.DC_Description)) AS Category,
DCG.DC_CatCode AS GenericCategory,
LTRIM(RTRIM(DCG.DC_Description)) AS GenericCategory,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93762 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle1,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93786 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle2,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93787 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle3,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93801 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle4
FROM NVDStandardEquipment SE (NOLOCK)
INNER JOIN NVDDictionaryOption DO (NOLOCK) ON DO.DO_OptionCode = SE.SE_optionCode
INNER JOIN NVDDictionaryOptionGenericCatLink DOGCL (NOLOCK) ON DOGCL.OGCL_OptionCatCode = DO.DO_CatCode
LEFT JOIN NVDDictionaryOptionGenericLink DOGL ON DOGL.ogl_optioncode = SE.SE_optionCode
LEFT JOIN NVDDictionaryGeneric DG ON DG.DG_GenericCode = DOGL.OGL_genericCode
INNER JOIN NVDDictionaryCategory DCS (NOLOCK) ON DCS.DC_CatCode = DO.DO_CatCode
INNER JOIN NVDDictionaryCategory DCG (NOLOCK) ON DCG.DC_CatCode = DOGCL.OGCL_GenericCatCode
WHERE SE.SE_id in (93762,93786,93787,93801)
This will return a result set as in the attached image.
In my stored procedure I am declaring a temporary table where I store all the Ids received from the backend, somehow I would have to loop through these (Not sure if thats the best approach), I don't know whether is possible to generate the same output based on a ID list to generate the columns as per the example, has anyone done something similar or have some ideas about how I could achieve this?
Thanks in advance!
Let's say you need to update/maintain the following table.
CREATE TABLE [dbo].[Test1](
[MemberID] [varchar](50) NULL,
[InpatientDays] [varchar](50) NULL,
[ERVisits] [varchar](50) NULL,
[OfficeVisits] [varchar](50) NULL,
[Narcotics] [varchar](50) NULL,
[DaysSinceLastERVisit] [varchar](50) NULL,
[Pain] [varchar](50) NULL,
[TotalVisits] [varchar](50) NULL
) ON [PRIMARY]
GO
You have a file with a list of the following IDs.
AcuteDrugGapSmall
ClaimLines
MedicalClaims
PoorCare
ProviderCount
StartedOnCombination
Create a table to save the IDs.
CREATE TABLE [dbo].[Test2](
[ID] [varchar](50) NULL
) ON [PRIMARY]
GO
After you insert the IDs run the following to generate the TSQL script to add the new columns. I'm assuming they're all VARCHAR(50) in this example. You can tweak to code given more information regarding data_type and character_maximum_length. You can take a similar approach to drop columns if needed. If you had a flag in table Test2 indicating whether to add or drop the column.
DECLARE #SQL VARCHAR(MAX)
SELECT #SQL = STRING_AGG('ALTER TABLE [dbo].[Test1] ADD ' + ID + ' VARCHAR(50)', ' ')
FROM [dbo].[Test3]
EXECUTE (#SQL)
The above script generates and executes the following. Please mark as solution if this works for you.
ALTER TABLE [dbo].[Test1] ADD AcuteDrugGapSmallVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD ClaimLinesVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD MedicalClaimsVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD PoorCareVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD ProviderCountVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD StartedOnCombinationVARCHAR(50)

SQL - MERGE INTO - from XML with children

I'm trying to write a SQL script to import data from an XML. In the database, I prepared 2 tables:
* CodeGroup with the following fields: Code / Description
CodeValue with the following fields: Code / Description / CodeGroupId (FK - int)
=> 1 CodeGroup have many CodeValue
Here's a part of the XML:
<MASTERDATA plant="SXB">
<CODEGROUPS>
<CODEGROUPC><CODEGROUP>ANODE</CODEGROUP><CODEGROUPX>Condition of anodes</CODEGROUPX>
<CODEVALUES>
<CODEVALUEC><CODEVALUE>02AD</CODEVALUE><CODEVALUEX>2-Minor Depletion / Damage</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>03AD</CODEVALUE><CODEVALUEX>3-Major Depletion / Plan to refurbish</CODEVALUEX></CODEVALUEC>
</CODEVALUES>
</CODEGROUPC>
<CODEGROUPC><CODEGROUP>AUTO</CODEGROUP><CODEGROUPX>Measurement method</CODEGROUPX>
<CODEVALUES>
<CODEVALUEC><CODEVALUE>00ND</CODEVALUE><CODEVALUEX>0-Inspection Not Done/not inspectable</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>01AU</CODEVALUE><CODEVALUEX>Automatic</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>01MA</CODEVALUE><CODEVALUEX>Manual</CODEVALUEX></CODEVALUEC>
</CODEVALUES>
</CODEGROUPC>
</CODEGROUPS>
</MASTERDATA>
I don't know if it's possible to write in a single MERGE INTO statement the ability to insert the values in CodeGroup and in CodeValue:
MERGE INTO [dbo].[CodeGroup] AS TARGET
USING
(
SELECT DISTINCT
d.x.value('../../#plant[1]', 'nvarchar(15)') as PlantId,
d.x.value('INGRP[1]', 'nchar(3)') as Code,
d.x.value('INGRPX[1]', 'nvarchar(20)') as Name
FROM #data.nodes('/MASTERDATA/CODEGROUPS/CODEGROUPC')as d(x)
)
AS SOURCE ON (SOURCE.Code= TARGET.Code
AND SOURCE.PlantId = TARGET.PlantId)
WHEN MATCHED
THEN UPDATE SET SOURCE.Name= TARGET.Name
WHEN NOT MATCHED BY TARGET
THEN INSERT (PlantId, Code, Name)
VALUES (SOURCE.PlantId, SOURCE.Code, SOURCE.Name)
WHEN NOT MATCHED BY SOURCE AND (TARGET.PlantId in (SELECT PlantId FROM #TempTable))
THEN DELETE;
That's what I prepared but I didn't find out how to and the "children" (CodeValue of a CodeGroup)
Thank you for your help
You are targeting two different tables. This cannot be done in a single statement.
Try this code:
DECLARE #xml XML=
'<MASTERDATA plant="SXB">
<CODEGROUPS>
<CODEGROUPC><CODEGROUP>ANODE</CODEGROUP><CODEGROUPX>Condition of anodes</CODEGROUPX>
<CODEVALUES>
<CODEVALUEC><CODEVALUE>02AD</CODEVALUE><CODEVALUEX>2-Minor Depletion / Damage</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>03AD</CODEVALUE><CODEVALUEX>3-Major Depletion / Plan to refurbish</CODEVALUEX></CODEVALUEC>
</CODEVALUES>
</CODEGROUPC>
<CODEGROUPC><CODEGROUP>AUTO</CODEGROUP><CODEGROUPX>Measurement method</CODEGROUPX>
<CODEVALUES>
<CODEVALUEC><CODEVALUE>00ND</CODEVALUE><CODEVALUEX>0-Inspection Not Done/not inspectable</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>01AU</CODEVALUE><CODEVALUEX>Automatic</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>01MA</CODEVALUE><CODEVALUEX>Manual</CODEVALUEX></CODEVALUEC>
</CODEVALUES>
</CODEGROUPC>
</CODEGROUPS>
</MASTERDATA>';
SELECT #xml.value('(/MASTERDATA/#plant)[1]','varchar(100)') AS Masterdata_plant
,cGr.value('(CODEGROUP/text())[1]','varchar(100)') AS CodeGroup
,cGr.value('(CODEGROUPX/text())[1]','varchar(100)') AS CodeGroupX
,cVl.value('(CODEVALUE/text())[1]','varchar(100)') AS CodeValue
,cVl.value('(CODEVALUEX/text())[1]','varchar(100)') AS CodeValueX
INTO #tmpXmlData
FROM #xml.nodes('/MASTERDATA/CODEGROUPS/CODEGROUPC') A(cGr)
OUTER APPLY cGr.nodes('CODEVALUES/CODEVALUEC') B(cVl);
SELECT * FROM #tmpXmlData;
Use the result you find in #tmpXmlData to run MERGE statements against both tables separately.
Shnugo through out the challenge, when he said it was impossible to do this in one MERGE statement. And he was right, I think. However, it got me thinking because I have used the OUTPUT clause many times to solve this type of problem, which I think is a MASTER - DETAIL merge, when the key of the master table is not the business key and is an IDENTITY() or NEWID().
What is the most efficient way of doing what you want to do? Shnugo gave you a partial solution, so I have enhanced it (thanks and credit to Shnugo) to give you what you want but it uses an INSERT into temp table, then two MERGE statements. One for the master and one for the detail table.
The first MERGE uses a trick that allows you to retrieve the new IDENTITY field of the new rows, so that you don't have to join back to the master table by the business keys when doing the detail table. I hope this helps.
Here's my code:
Assumptions:
I have put NOT NULLs on several fields.
I have put a CASCADE DELETE on the foreign key reference.
create table dbo.CodeGroup (
CodeGroupId int identity(1,1) primary key,
Masterdata_plant varchar(100) not null,
Code varchar(100) not null,
Description varchar(100) not null
)
go
CREATE NONCLUSTERED INDEX [IX_CodeGroup] ON dbo.CodeGroup
(
[Masterdata_plant] ASC,
[Code] ASC
)
go
create table dbo.CodeValue
(
Code varchar(100) not null,
CodeGroupId int not null,
Description varchar(100) not null,
CONSTRAINT PK_CodeValue PRIMARY KEY CLUSTERED
(
Code ASC,
CodeGroupId ASC
)
)
go
ALTER TABLE dbo.CodeValue WITH CHECK ADD CONSTRAINT FK_CodeValue_CodeGroup FOREIGN KEY(CodeGroupId)
REFERENCES dbo.CodeGroup (CodeGroupId)
ON DELETE CASCADE
go
ALTER TABLE dbo.CodeValue CHECK CONSTRAINT FK_CodeValue_CodeGroup
go
delete from dbo.CodeGroup
go
delete from dbo.CodeValue
go
DECLARE #data XML=
'<MASTERDATA plant="SXB">
<CODEGROUPS>
<CODEGROUPC><CODEGROUP>ANODE</CODEGROUP><CODEGROUPX>Condition of anodes</CODEGROUPX>
<CODEVALUES>
<CODEVALUEC><CODEVALUE>02AD</CODEVALUE><CODEVALUEX>2-Minor Depletion / Damage</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>03AD</CODEVALUE><CODEVALUEX>3-Major Depletion / Plan to refurbish</CODEVALUEX></CODEVALUEC>
</CODEVALUES>
</CODEGROUPC>
<CODEGROUPC><CODEGROUP>AUTO</CODEGROUP><CODEGROUPX>Measurement method</CODEGROUPX>
<CODEVALUES>
<CODEVALUEC><CODEVALUE>00ND</CODEVALUE><CODEVALUEX>0-Inspection Not Done/not inspectable</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>01AU</CODEVALUE><CODEVALUEX>Automatic</CODEVALUEX></CODEVALUEC>
<CODEVALUEC><CODEVALUE>01MA</CODEVALUE><CODEVALUEX>Manual</CODEVALUEX></CODEVALUEC>
</CODEVALUES>
</CODEGROUPC>
</CODEGROUPS>
</MASTERDATA>'
if object_id('tempdb..#tmpXmlData') is not null Begin
drop table #tmpXmlData
End
create table #tmpXmlData (
ExistingCodeGroupId int,
Plant varchar(100),
CodeGroup varchar(100),
Description varchar(100),
CodeValue varchar(100),
CodeValueDescription varchar(100)
)
insert into #tmpXmlData
SELECT gc.CodeGroupId
,#data.value('(/MASTERDATA/#plant)[1]','varchar(100)') AS Plant
,cGr.value('(CODEGROUP/text())[1]','varchar(100)') AS CodeGroup
,cGr.value('(CODEGROUPX/text())[1]','varchar(100)') AS Description
,cVl.value('(CODEVALUE/text())[1]','varchar(100)') AS CodeValue
,cVl.value('(CODEVALUEX/text())[1]','varchar(100)') AS CodeValueDescription
FROM #data.nodes('/MASTERDATA/CODEGROUPS/CODEGROUPC') A(cGr)
OUTER APPLY cGr.nodes('CODEVALUES/CODEVALUEC') B(cVl)
left join CodeGroup gc
on gc.Masterdata_plant = #data.value('(/MASTERDATA/#plant)[1]','varchar(100)')
and gc.Code = cGr.value('(CODEGROUP/text())[1]','varchar(100)')
SELECT * FROM #tmpXmlData;
if object_id('tempdb..#IDs') is not null Begin
drop table #IDs
end
create table #IDs(
CodeGroupID int,
Plant varchar(100),
Code varchar(100),
Description varchar(100)
)
go
CREATE NONCLUSTERED INDEX [IX_CodeGroup] ON #IDs
(
[Plant] ASC,
[Code] ASC
)
;MERGE INTO dbo.CodeGroup AS TARGET
USING
(
select ExistingCodeGroupId, Plant, CodeGroup,Description
from #tmpXmlData
group by ExistingCodeGroupId, Plant, CodeGroup,Description
)
AS SOURCE ON (SOURCE.ExistingCodeGroupId= TARGET.CodeGroupId)
--Plant, and CodeGroup must have matched already in left join above.
WHEN MATCHED
and TARGET.Description <> SOURCE.Description
THEN UPDATE SET TARGET.Description = SOURCE.Description
WHEN NOT MATCHED BY TARGET
THEN INSERT (Masterdata_plant, Code, Description)
VALUES (SOURCE.Plant, SOURCE.CodeGroup, SOURCE.Description)
WHEN NOT MATCHED BY SOURCE
THEN DELETE
output inserted.*
into #IDs; --This gets us the new CodeGroupId's
select * from #IDs
;MERGE INTO dbo.CodeValue AS TARGET
USING
(
select CodeGroupID=coalesce(d.ExistingCodeGroupId,i.CodeGroupID), CodeValue, CodeValueDescription
from #tmpXmlData d
left join #IDs i
on i.Plant = d.Plant
and i.Code = d.CodeGroup
group by coalesce(d.ExistingCodeGroupId,i.CodeGroupID), CodeValue, CodeValueDescription
)
AS SOURCE ON (SOURCE.CodeGroupID= TARGET.CodeGroupID
and SOURCE.CodeValue = Target.Code)
WHEN MATCHED
and TARGET.Description <> SOURCE.CodeValueDescription
THEN UPDATE SET TARGET.Description = SOURCE.CodeValueDescription
WHEN NOT MATCHED BY TARGET
THEN INSERT (CodeGroupID, Code, Description)
VALUES (SOURCE.CodeGroupID, SOURCE.CodeValue, SOURCE.CodeValueDescription)
WHEN NOT MATCHED BY SOURCE
THEN DELETE
output inserted.*;
select * from dbo.CodeGroup
select * from dbo.CodeValue

Stored procedure to Insert data between tables

I want to insert data from a table called temp_menu into another called menu.
They have the same structure, they store the same data, I want to create a stored procedure to check the differences between the tables. If there are any different rows and the rows don't exist in table menu, I want to insert them into menu; if the rows exists, I want to update the rows in menu if the DateReg column is higher that the DateReg column in the temp_menu table.
The tables have this structure:
CREATE TABLE [dbo].[Menu_Temp]
(
[Date] [datetime] NOT NULL,
[Ref] [int] NOT NULL,
[Art] [char](60) NOT NULL,
[Dish] [char](60) NOT NULL,
[DateReg] [datetime] NOT NULL,
[Zone] [char](60) NOT NULL,
);
I have this code to check for differences between the tables:
SELECT *
INTO #diffs
FROM [Regi].dbo.menu
EXCEPT
SELECT * FROM [Regi].dbo.menu_Temp
IF ##ROWCOUNT = 0
RETURN
SELECT * FROM #diffs
Full details are here : https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
An example for your situation could be...
MERGE
[Regi].dbo.menu
USING
[Regi].dbo.menu_Temp
ON (menu_Temp.[Ref] = menu.[Ref]) -- Assumes [Ref] is the identifying column?
WHEN
MATCHED AND (menu_Temp.[DateReg] > menu.[DateReg])
THEN
UPDATE SET [Art] = menu_Temp.[Art],
[Dish] = menu_Temp.[Dish],
[Zone] = menu_Temp.[Zone],
[Date] = menu_Temp.[Date],
[DateReg] = menu_Temp.[DateReg]
WHEN
NOT MATCHED
THEN
INSERT (
[Date],
[Ref],
[Art],
[Dish],
[DateReg],
[Zone]
)
VALUES (
menu_Temp.[Date],
menu_Temp.[Ref],
menu_Temp.[Art],
menu_Temp.[Dish],
menu_Temp.[DateReg],
menu_Temp.[Zone]
)
http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b47d7e879856ffe6210589f6bb64829f

Why does my trigger to avoid duplicate rows not work?

Before anyone suggests a unique index or key, I have a very good case for this.
I am using this trigger, from Trigger to prevent Insertion for duplicate data of two columns:
CREATE TRIGGER LogDuplicates ON bkPersonPoints
FOR INSERT
AS
if exists (select * from bkPersonPoints c
inner join inserted i
on c.Name = i.Name and c.Points = i.Points)
begin
rollback
end
GO
That answer is accepted and has 15 up-votes, so I would expect it to work, yet even on my very first insert, into an empty table:
insert bkPersonPoints (Name, Points) values ('Brady', 100)
I get the error:
The transaction ended in the trigger. The batch has been aborted.
APPENDIX: The table looks like this:
CREATE TABLE [dbo].[bkPersonPoints](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Points] [int] NOT NULL
) ON [APP_BR2_User]
This is happening because it is detecting the record you're currently inserting to the table. You need to filter that out of the EXISTS clause:
CREATE TRIGGER LogDuplicates ON bkPersonPoints
FOR INSERT
AS
if exists (select * from bkPersonPoints c
inner join inserted i
on c.Name = i.Name
and c.Points = i.Points
and c.id <> i.id)
begin
rollback
end
GO
Anyway, suggest CONSTRAINT
ALTER TABLE bkPersonPoints
ADD CONSTRAINT c_uniq_Name_and_points UNIQUE (Name, Points)
This answer was inspired by one posted on Apr 13 '20 at 18:34 in Trigger to prevent Insertion for duplicate data of two columns.
CREATE TRIGGER MyTrigger ON dbo.MyTable
INSTEAD OF INSERT
AS
if not exists (
select * from MyTable t
inner join inserted i
on i.name=t.name and i.date=t.date and i.id <> t.id )
begin
Insert into MyTable (Name, Date) Select Name, Date from inserted
end
else
THROW 51000, 'Statement terminated because a duplicate was found for the object', 1;
go

Stored procedure to update column based on Min Date

I have been able to find similar issues to one I am having, but not that helps me to resolve!
I have a table that contains job data (tablea), schema as follows:
CREATE TABLE [dbo].[tablea](
[ID] [int] IDENTITY(1,1) NOT NULL,
[JobNo] [varchar](32) NOT NULL,
[JobDesc] [varchar](255) NULL,
[RequiredDate] [datetime] NULL
Certain jobs within this table provide components for other jobs in the same table, going forward I will call the jobs that provide components "Production Jobs" and the job receiving the components "sub jobs" - there is nothing within the table that links the rows.
There is another table (tableb)
CREATE TABLE [dbo].[tableb](
[ID] [int] IDENTITY(1,1) NOT NULL,
[JobNo] [varchar](32) NOT NULL CONSTRAINT [DF_JobProducts_JobNo] DEFAULT (''),
[SubJobNo] [varchar](32) NULL
This contains the "production job" job number (tableb.JobNo), and in the same row, the job number of the "sub job" (tableb.SubJobNo)
nb if a "production job" provides components for 7 x "sub jobs" there will be 7 x rows in tableb
I have an objective to execute a stored procedure that will update the required date column of the "production job" row of tablea with the EARLIEST date of the required date column of the associated "sub job". When executing the stored procedure, a unique identifier will be used as the variable - this will be the title of the "production job".
My attempt is as follows:
CREATE PROCEDURE [dbo].[UpdateDate]
#Title varchar(32),
AS
BEGIN
SET NOCOUNT ON;
UPDATE ta1
SET ta1.RequiredDate = ta2.RequiredDate
FROM tablea ta1
INNER JOIN tableb tb ON ta1.JobNo = tb.JobNo
INNER JOIN
(SELECT JobNo, MIN(RequiredTime)
GROUP BY JobNo
FROM tablea) ta2 ON tb.SubJobNo = ta2.JobNo
WHERE ta1.JobDesc = #Title
This has got me stumped so any help or pointers would be greatly appreciated!
If you are trying to say that tabalea relates to tableb then back to tablea to get to required time then just change the cte to something like this:
CREATE PROCEDURE [dbo].[UpdateDate]
#Title varchar(32),
AS
BEGIN
SET NOCOUNT ON;
;WITH cte AS (
SELECT
ta.JobNo
,MIN(ta2.RequiredTime) as NewRequiredDate
FROM
tablea ta
INNER JOIN tableb tb
ON ta.JobNo = tb.JobNo
INNER JOIN tablea ta2
ON tb.SubJobNo = ta2.JobNo
WHERE
ta.JobDesc = #Title
GROUP BY
ta.JobNo
)
UPDATE ta
SET RequiredDate = c.NewRequiredDate
FROM tablea ta
INNER JOIN cte c
ON ta.JobNo = c.JobNo