T-SQL : execution of stored procedure - sql-server

I have a procedure which is supposed to save data into a temporary table and at the end of it delete the old records and insert the new ones into the real table.
The end of the procedure is as follows:
...
--THIS SELECT HAS THE CORRECT DATA
SELECT * FROM #NewTempTable;
BEGIN TRY
BEGIN TRANSACTION;
--DELETE OLD COMPONENTS
DELETE FROM AlteracaoEngenharia.hComponents
WHERE Id IN (SELECT C.Id
FROM AlteracaoEngenharia.hComponents AS C
JOIN AlteracaoEngenharia.hEngineeringChanges AS EC ON C.EngineeringChangeId = EC.Id
WHERE C.Name NOT LIKE '%(Manual)%' AND EC.State LIKE 'Aberto');
INSERT INTO AlteracaoEngenharia.hComponents (Name, OldQuantity, Quantity, CDP, Value, Type, EngineeringChangeId)
(SELECT
Name, OldQuantity, Quantity, CDP, Value, Type, EngineeringChangeId
FROM
#NewTempTable);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
END CATCH
END
If I select all the procedure until the begin try and f5(execute it) it it gives no errors and I get the expected data on #NewTempTable; But when save the procedures and try to Execute it I get the following error
While #NewTempTable has the expected data, the execution of the procedure returns -6 and The data doesn't get inserted into AlteracaoEngenharia.hComponents.
None of the data I receive is null, neither are the AlteracaoEngenharia.hComponents columns not nullable.
I also have the same procedure in a TestServer exactly equal (some connections are different, but if that would be the mistake I would get no data) and that procedure works fine.
I know I'm not showing most of the procedure, buts it's a bit complex and with sensible information. I'm hoping I can get some pointers on the right direction with what I have described.
PS: I have tried with both #newTempTable and ##newTempTable

Try below code bock:
BEGIN TRANSACTION;
BEGIN TRY
--DELETE OLD COMPONENTS
Delete From AlteracaoEngenharia.hComponents where Id in
(Select C.Id From AlteracaoEngenharia.hComponents as C
Join AlteracaoEngenharia.hEngineeringChanges as EC on C.EngineeringChangeId = EC.Id
Where C.Name not like '%(Manual)%' and EC.State like 'Aberto');
Insert Into AlteracaoEngenharia.hComponents (Name, OldQuantity, Quantity, CDP, Value, Type, EngineeringChangeId)
(Select Name, OldQuantity, Quantity, CDP, Value, Type, EngineeringChangeId From #NewTempTable);
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
throw;
END
END CATCH;
IF ##TRANCOUNT > 0
BEGIN
COMMIT TRANSACTION;
PRINT 'Successfully Completed'
END

The best way to ensure a full ROLLBACK after an error is to use XACT_ABORT, this forces all errors to abort the batch, and rollback the transaction:
SET XACT_ABORT ON;
SET NOCOUNT ON;
BEGIN TRANSACTION;
--DELETE OLD COMPONENTS
DELETE FROM AlteracaoEngenharia.hComponents
WHERE Id IN (SELECT C.Id
FROM AlteracaoEngenharia.hComponents AS C
JOIN AlteracaoEngenharia.hEngineeringChanges AS EC ON C.EngineeringChangeId = EC.Id
WHERE C.Name NOT LIKE '%(Manual)%' AND EC.State LIKE 'Aberto');
INSERT INTO AlteracaoEngenharia.hComponents (Name, OldQuantity, Quantity, CDP, Value, Type, EngineeringChangeId)
(SELECT
Name, OldQuantity, Quantity, CDP, Value, Type, EngineeringChangeId
FROM
#NewTempTable);
COMMIT TRANSACTION;
No TRY/CATCH or ROLLBACK is necessary here, any error automatically rolls back.

Related

T-SQL Insert into in the BEGIN CATCH

Thanks in advance for the help.
What I'm trying to achieve is handling the constraint violation of the FK (Municipality code) and when that's the case I want to insert the record in a fallout table.
In this block of code there result of the select can be null and therefore will throw an exception as I have a not null clause on the target.
TARGET.MUNICIPALITYCODE = (SELECT m.MUNICIPALITYCODE FROM MUNICIPALITY m WHERE SOURCE.MUNICIPALITYCODE = m.MUNICIPALITYCODE)
I wanted to be able to treat the exception on the BEGIN CATCH block and INSERT into a table of my choice the values that I was using on the SOURCE.
Does anyone knows if it is possible?
CREATE PROCEDURE upsertStagingToStreet
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRY
BEGIN TRAN
MERGE STREET AS TARGET
USING STREET_STAGING AS SOURCE
ON (TARGET.correlationkey = SOURCE.correlationkey)
WHEN MATCHED
THEN UPDATE SET TARGET.Qty = SOURCE.Qty,
TARGET.MUNICIPALITYCODE = (SELECT m.MUNICIPALITYCODE FROM MUNICIPALITY m WHERE SOURCE.MUNICIPALITYCODE = m.MUNICIPALITYCODE)
WHEN NOT MATCHED BY TARGET
THEN INSERT (MUNICIPALITYCODE, STREENAME,STREECODE) VALUE ((SELECT m.MUNICIPALITYCODE FROM MUNICIPALITY m WHERE SOURCE.MUNICIPALITYCODE = m.MUNICIPALITYCODE),SOURCE.STREETCODE,SOURCE.STREETCODE)
COMMIT
END TRY
begin catch
# I'm not able to figure this part out.
INSERT INTO STREET_FALLOUT (MUNICIPALITYCODE, STREENAME,STREECODE,ERRORREASON) VALUES (SOURCE.MUNICIPALITYCODE,STREETNAME,STREETCODE,ERROR_MESSAGE())
end catch
You are approaching this wrong. It is not possible to insert some of the rows and catch errors on others.
Instead, just query the non-matching rows, and merge only the matching ones.
Note the lack of error-handling, and the inclusion of XACT_ABORT ON. This is the correct way, as all errors will cause the transaction to rollback anyway.
Note the SOURCE table in the merge is pre-joined with MUNICIPALITY, so only matching rows can appear.
A MERGE statement must have a semi-colon terminator, which is good practice anyway.
CREATE PROCEDURE upsertStagingToStreet
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRAN
INSERT INTO STREET_FALLOUT
(MUNICIPALITYCODE, STREENAME, STREECODE, ERRORREASON)
SELECT
SOURCE.MUNICIPALITYCODE,
SOURCE.STREETCODE,
SOURCE.STREETCODE,
'Missing MUNICIPALITYCODE'
FROM STREET_STAGING AS SOURCE
WHERE NOT EXISTS (SELECT 1
FROM MUNICIPALITY m
WHERE SOURCE.MUNICIPALITYCODE = m.MUNICIPALITYCODE
);
WITH SOURCE AS (
SELECT SOURCE.*
FROM STREET_STAGING AS SOURCE
JOIN MUNICIPALITY m ON SOURCE.MUNICIPALITYCODE = m.MUNICIPALITYCODE
)
MERGE STREET AS TARGET
USING SOURCE
ON (TARGET.correlationkey = SOURCE.correlationkey)
WHEN MATCHED THEN
UPDATE SET
Qty = SOURCE.Qty,
MUNICIPALITYCODE = SOURCE.MUNICIPALITYCODE
WHEN NOT MATCHED BY TARGET THEN
INSERT (MUNICIPALITYCODE, STREENAME, STREECODE)
VALUES (SOURCE.MUNICIPALITYCODE, SOURCE.STREETCODE, SOURCE.STREETCODE)
;
COMMIT;
GO

What is the correct way of using ##IDENTITY

Question regarding the ##IDENTITY, I have 4 different tables:
Customer [Id]
Person [Id, fname, lname]
Account [Cd, owner, balance]
Transaction [Id, account, type]
Customer Id has a feature of identity increment 1.
My goal is to create a new person for the database, so that
Customer.Id = Person.Id = Account.owner = Transaction.ID
I have tried the following below, however I get this error:
Cannot insert null value into column owner
How do I correct the mistakes to make it work?
BEGIN TRAN
BEGIN TRY
INSERT INTO bank.customer DEFAULT VALUES
INSERT INTO bank.person (id, fname, lname)
VALUES (##IDENTITY, 'Mike', 'Phelps')
INSERT INTO bank.account (cd, owner, balance)
VALUES (2, ##IDENTITY, 0)
INSERT INTO bank.transaction (id, account, type)
VALUES (##IDENTITY, (SELECT cd FROM pankki.tili,'P')
END TRY
BEGIN CATCH
ROLLBACK
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH
I suspect what you want is this:
BEGIN TRY
BEGIN TRAN;
DECLARE #ID int; --bigint, decimal?
INSERT INTO bank.customer DEFAULT VALUES;
SET #ID = SCOPE_IDENTITY(); --Assumes customer has a column with an IDENTITY
INSERT INTO bank.person (id,fname,lname)
VALUES(#ID,'Mike','Phelps');
INSERT INTO bank.account (cd,owner,balance)
VALUES(2,#ID,0);
INSERT INTO bank.transaction(id,account,type)
SELECT #ID,
cd,
'P'
FROM pankki.tili; --I assume, therefore, that pankki.tili only ever has 1 row
COMMIT; --YOu were missing this
END TRY
BEGIN CATCH
ROLLBACK;
THROW; --Don't SELECT the error details, THROW it.
END CATCH
From the Microsoft document:
After an INSERT, SELECT INTO, or bulk copy statement is completed, ##IDENTITY contains the last identity value that is generated by the statement. If the statement did not affect any tables with identity columns, ##IDENTITY returns NULL.
I take it PERSON does not have an identity column, so when you insert into it, ##identity becomes NULL.
If you want to user the ##identity from the insert for the other tables, use it to set the value of a variable.
declare #PersistentID int;
INSERT INTO bank.customer DEFAULT VALUES
set #PersistentID = ##IDENTITY -- or scope_identity() is safer
INSERT INTO bank.person (id,fname,lname)
VALUES( #PersistentID ,'Mike','Phelps')
First, you need to understand the difference between this two commands:
##identity returns the last inserted identity value in ANY table in the current session, regardless of scope.
IDENT_CURRENT('table_or_view') returns the last inserted identity value for a GIVEN table.
So, for your case, you need to use the second one.
And your script would be something like this:
BEGIN TRAN
BEGIN TRY
INSERT INTO bank.customer DEFAULT VALUES
SET #customerID = (SELECT IDENT_CURRENT('bank.customer'))
INSERT INTO bank.person (id,fname,lname)
VALUES( #customerID,'Mike','Phelps')
INSERT INTO bank.account (cd,owner,balance)
VALUES(2,#customerID,0)
INSERT INTO bank.transaction(id,account,type)
VALUES(#customerID,(SELECT cd FROM pankki.tili,'P')
END TRY
This way you can guarantee that the same ID in inserted in the four tables.
If you are using ##identity this value is changing with every new insert.

Where is the issue in this trigger?

I need your expert observation to make this trigger work.
I have 3 tables, Create_Event is main, 2nd (events_status) is status table and 3rd (event_history) one is 'backup table'.
I just want to keep a track of every events as the status is updated to 'Completed' also want to delete the same row from the Main table but I don't want to lose it until I have a track.
So, here is that what I am trying..but its delete part is not working.
ALTER TRIGGER trgUpdhistory on Events_Status for update
as
declare #status varchar(255)
BEGIN
set xact_abort on
select #status=status from inserted
if (#status = 'Completed')
begin try
begin tran
insert into Event_History
select * from Create_Event
where exists(select * from create_event D left join inserted E on D.ID=E.CE_Ids)
delete from Create_Event
where exists(select * from create_event D left join deleted E on D.ID=E.CE_Ids)
COMMIT
end try
begin catch
ROLLBACK
RAISERROR ('Transaction is not completed',16,1)
end catch
END

Get Key values of the Row whose values are not Updated during Multiple row update

This is a Continuation of my previous question
sql update for dynamic row number
This time I am having an updated requirement.
I am having 2 tables
CraftTypes & EmployeeCraftTypes.
I need to update multiple rows in the CraftType Table and
I was able to update it as per the answer provided by TheGameiswar
Now there is a modification in the requirement.
In the table CraftTypes, there is a foreign key reference for the column CraftTypeKey with the table EmployeeCraftsTypes.
If there exist an entry for CraftTypeKey in the EmployeeCrafttypes table, then the row should not be updated.
Also the CraftTypeKey's whose row values are not updated must be obtained for returning the FK_restriction status of the rows.
This is the sql query I am using.
CREATE TYPE [DBO].[DEPARTMENTTABLETYPE] AS TABLE
( DepartmentTypeKey SMALLINT, DepartmentTypeName VARCHAR(50),DepartmentTypeCode VARCHAR(10) , DepartmentTypeDescription VARCHAR(128) )
ALTER PROCEDURE [dbo].[usp_UpdateDepartmentType]
#DEPARTMENTDETAILS [DBO].[DEPARTMENTTABLETYPE] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #rowcount1 INT
BEGIN
BEGIN TRY
BEGIN TRANSACTION
UPDATE D1
SET
D1.[DepartmentTypeName]=D2.DepartmentTypeName
,D1.[DepartmentTypeCode]=D2.DepartmentTypeCode
,D1.[DepartmentTypeDescription]=D2.DepartmentTypeDescription
FROM
[dbo].[DepartmentTypes] D1
INNER JOIN
#DEPARTMENTDETAILS D2
ON
D1.DepartmentTypeKey=D2.DepartmentTypeKey
WHERE
D2.[DepartmentTypeKey] not in (select 1 from [dbo].[EmployeeDepartment] where [DepartmentTypeKey]=D2.DepartmentTypeKey)
SET #ROWCOUNT1=##ROWCOUNT
COMMIT
END TRY
BEGIN CATCH
SET #ROWCOUNT1=0
ROLLBACK TRAN
END CATCH
IF #rowcount1 =0
SELECT -174;
ELSE
SELECT 100;
END
END
Please Help
And Thanks in Advance
Ok
I think I figured out a way for it this time. I am not sure this is the right way, but its enough for me to meet the requirements.
I selected the distinct rows with FK reference from EmployeeCraftsTypes table as a second select query.
Now I can get the Row values which are not getting updated due to FK constraint.
This is the sql query I have used
ALTER PROCEDURE [dbo].[usp_UpdateCraftType]
#CRAFTDETAILS [DBO].[CRAFTTABLETYPE] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #STATUSKEY TINYINT = (SELECT DBO.GETSTATUSKEY('ACTIVE'))
DECLARE #ROWCOUNT1 INT
BEGIN
BEGIN TRY
BEGIN TRANSACTION
UPDATE C1
SET
[C1].[CraftTypeName]=C2.CRAFTTYPENAME
,[C1].[CRAFTTYPEDESCRIPTION]=C2.CRAFTTYPEDESCRIPTION
,[C1].[StatusKey]=C2.[StatusKey]
FROM
[dbo].[CRAFTTYPES] C1
INNER JOIN
#CRAFTDETAILS C2
ON
C1.CRAFTTYPEKEY=C2.CRAFTTYPEKEY
WHERE
C2.[CRAFTTYPEKEY] NOT IN (SELECT EC.[CRAFTTYPEKEY] from [dbo].[EmployeeCrafts] EC where EC.[CRAFTTYPEKEY]=C2.[CRAFTTYPEKEY])
SET #ROWCOUNT1=##ROWCOUNT
COMMIT
END TRY
BEGIN CATCH
SET #ROWCOUNT1=0
ROLLBACK TRAN
END CATCH
--SET #ROWCOUNT1 = ##ROWCOUNT
IF #ROWCOUNT1 =0
SELECT -172;
ELSE
BEGIN
SELECT 100;
SELECT DISTINCT EC.[CRAFTTYPEKEY],'Value Already Assigned' as Reason
FROM [DBO].[EmployeeCrafts] EC
JOIN #CRAFTDETAILS C3
on C3.[CRAFTTYPEKEY]=EC.[CRAFTTYPEKEY]
WHERE EC.[CRAFTTYPEKEY]=C3.[CRAFTTYPEKEY]
END
END
END
Now in the Web API side I can check if there is any update failure by checking the rowcount for the second table.
If the row count is more than 0, then update error message can be generated
Hope it will be helpful to someone ....

Select a column from a row and update the column to another value

I am trying to write a stored procedure that reads a column in a particular row of a table, then updates that column with a new value. The orig. is returned.
I want it to lock the row from others till I am done. What is the process?
I have something like
CREATE PROCEDURE [dbo].[aptc_Prt_NextDocumentNumberGet]
(#_iFormatConfigID INT, #_oNextDocumentNumber FLOAT OUTPUT)
AS
BEGIN
DECLARE #FrameworkConfig XML
SET #_oNextDocumentNumber = - 1
DECLARE #NewNextDocumentID FLOAT
SELECT
#_oNextDocumentNumber = FrameworkConfig.value('(/Parameters/Parameter[#Name="NextDocNo.NextDocumentNumber"])[1]', 'float')
FROM
[ttcPrtFormatConfig] WITH (ROWLOCK)
WHERE
FormatConfigID = #_iFormatConfigID
-- Select the Next Doc num out of the xml field
-- increment appropriate control and set output
IF #_iFormatConfigID IS NOT NULL
BEGIN
-- set what will be the "next" doc number after we add this current txn
IF (ABS(#_oNextDocumentNumber - 99999999999999999) < 0.0001)
BEGIN
SELECT #NewNextDocumentID = 1
END
ELSE
BEGIN
SELECT #NewNextDocumentID = #_oNextDocumentNumber + 1
END
UPDATE [ttcPrtFormatConfig]
WITH (ROWLOCK)
SET FrameworkConfig.modify('
replace value of
(/Parameters/Parameter[#Name="NextDocNo.NextDocumentNumber"]/text())[1]
with sql:variable("#NewNextDocumentID")')
WHERE FormatConfigID = #_iFormatConfigID
END
END
This should get you close to what you want.
DECLARE #MyValue INT
--You need a transaction so that the scope of your lock is well defined
BEGIN TRANSACTION
BEGIN TRY
--Get the value you are interested in, This select will lock the row so other people will not even be able to read it until you are finished!!!!!
SELECT #MyValue = MyValue
FROM MyTable WITH (UPDLOCK HOLDLOCK)
WHERE MyValue = SomeValue
--Do your checks and updates. You can take as long as you like as you are the only person who can do a read or update of this data.
IF
BEGIN
UPDATE MyTable
END
--Make sure you commit or rollback! this will release the lock
END TRY
BEGIN CATCH
--Oh no bad stuff! give up and put it back to how it was
PRINT ERROR_MESSAGE() + N' Your message here'
--Check there is a transaction that we can rollback
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK;
END
--You may want to return some error state and not throw!
THROW;
--RETURN -1 --(for example)
END CATCH;
--yay it all worked and your lock will be released
COMMIT
--Do what you like with the old value
RETURN #MyValue

Resources