I'm noob at stored procedures, I have created a stored procedure that take some parameters from C# page, then send back result as OUTPUT parameter.
I need to do some calculation to get end date, so I end up using a lot of IF statements. However, when I create the stored procedure I get error that I didn't know how to solve, every thing seems correct!.
Here is the stored procedure code:
CREATE PROCEDURE sp_RenewSubscription
-- Add the parameters for the stored procedure here
#Reference nvarchar(100),
#SubscribtionID nvarchar(100),
#Months int,
#Result nvarchar(200) OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #EndDate as nvarchar;
DECLARE #MonthCounts as int;
IF NOT EXISTS(SELECT [Reference] FROM [Norton].[dbo].[SubscriptionStatus] WHERE [Reference] = #Reference)
SET #Result = '0: Reference ID not found'
ELSE
IF NOT EXISTS(SELECT [Reference] FROM [Norton].[dbo].[SubscriptionStatus] WHERE [Reference] = #Reference AND [SubscribtionID] = #SubscribtionID)
SET #Result = '0: Subscribtion ID not found'
ELSE
BEGIN
SELECT TOP 1 #EndDate = [EndDate], #MonthCounts = [SubscriptionMonthCount] FROM [Norton].[dbo].[SubscriptionStatus] WHERE [Reference] = #Reference AND [SubscribtionID] = #SubscribtionID
IF #EndDate = '0'
BEGIN
UPDATE [Norton].[dbo].[SubscriptionStatus]
SET [SubscriptionMonthCount] = #Months + #MonthCounts
WHERE [Reference] = #Reference AND [SubscribtionID] = #SubscribtionID
END
ELSE
BEGIN
UPDATE [Norton].[dbo].[SubscriptionStatus]
SET [SubscriptionMonthCount] = #Months
WHERE [Reference] = #Reference AND [SubscribtionID] = #SubscribtionID
END
SET #Result = '1: Done Successfully'
END
GO
END
GO
This is error I got:
Msg 102, Level 15, State 1, Procedure sp_RenewSubscription, Line 44
Incorrect syntax near 'END'.
Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'END'.*
Thanks,
Remove the go from before the end of the procedure.
ie
END
SET #Result = '1: Done Successfully'
END
GO --- <-- get rid of this
END
Try this:
CREATE PROCEDURE Sp_renewsubscription
-- Add the parameters for the stored procedure here
#Reference NVARCHAR(100),
#SubscribtionID NVARCHAR(100),
#Months INT,
#Result NVARCHAR(200) output
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET nocount ON;
DECLARE #EndDate AS NVARCHAR;
DECLARE #MonthCounts AS INT;
IF NOT EXISTS(SELECT [reference]
FROM [Norton].[dbo].[subscriptionstatus]
WHERE [reference] = #Reference)
SET #Result = '0: Reference ID not found'
ELSE IF NOT EXISTS(SELECT [reference]
FROM [Norton].[dbo].[subscriptionstatus]
WHERE [reference] = #Reference
AND [subscribtionid] = #SubscribtionID)
SET #Result = '0: Subscribtion ID not found'
ELSE
BEGIN
SELECT TOP 1 #EndDate = [enddate],
#MonthCounts = [subscriptionmonthcount]
FROM [Norton].[dbo].[subscriptionstatus]
WHERE [reference] = #Reference
AND [subscribtionid] = #SubscribtionID
IF #EndDate = '0'
UPDATE [Norton].[dbo].[subscriptionstatus]
SET [subscriptionmonthcount] = #Months + #MonthCounts
WHERE [reference] = #Reference
AND [subscribtionid] = #SubscribtionID
ELSE
UPDATE [Norton].[dbo].[subscriptionstatus]
SET [subscriptionmonthcount] = #Months
WHERE [reference] = #Reference
AND [subscribtionid] = #SubscribtionID
SET #Result = '1: Done Successfully'
END
END
GO
Related
I am developing a stored procedure to update specific columns using parameters.
For example:
CREATE PROCEDURE save_proc
(
#userid varchar(5),
#column varchar(10),
#data varchar(50)
)
AS
BEGIN
UPDATE MyProfile
SET #column = #data
WHERE userid = #userid
The following is my code that I've tried but didn't work. Is it possible to do such an update by updating specific columns through parameters? Thanks
You can build up a query in your stored procedure and execute.
CREATE PROCEDURE save_proc
(
#userid varchar(5),
#column varchar(10),
#data varchar(50)
)
AS
BEGIN
exec sp_executesql N'UPDATE MyProfile SET ' + #column + '=''' + #data + ''' WHERE userid = ''' + #userid + ''''
END
The method may lead to security concern however.
Its doable but i would avoid doing it.... you code should be like below:
DECLARE #column varchar(10)
DECLARE #data varchar(50)
UPDATE DummyTable
SET
col1 = CASE WHEN #column = 'Col1' THEN #data ELSE col1 END ,
col2 = CASE WHEN #column = 'Col2' THEN #data ELSE col2 END
where userid = #userid
.
.
.
Hope that this is what you are looking for
Using a CASE statement causes all columns identified to once again be updated; this would invoke a reindexing for some indexes upon columns wherein that change, even if the same value, occurs. Waste of execution updates on server.
An IF statement is best option for single value, especially if value is NOT the same type as the column datatype.
IF(#fieldName IS NULL) SELECT #fieldName = ''
IF(#updateValue IS NULL) SELECT #updateValue = ''
DECLARE #myCount int
SELECT #myCount = count(*) FROM [dbo].[myTable] WHERE #inventoryID = #updateInventoryID
--if no valid data to update, return -1 for noUpdate
IF(0 = #updateInventoryID
OR 0 = #myCount
OR '' = #fieldName
OR '' = #updateValue)
RETURN -1
BEGIN TRAN
IF('notes' = #fieldName)
UPDATE [dbo].[myTable]
SET [notes] = #updateValue, [modifyDate] = GETDATE()
WHERE [inventoryID] = #updateInventoryID
IF('reorderPoint' = #fieldName)
BEGIN
SET #newValueInt = CONVERT(int, #updateValue)
UPDATE [dbo].[myTable]
SET [reorderPoint] = #newValueInt, [modifyDate] = GETDATE()
WHERE [inventoryID] = #updateInventoryID
END
IF('safetyStock' = #fieldName)
BEGIN
SET #newValueInt = CONVERT(int, #updateValue)
UPDATE [dbo].[myTable]
SET [safetyStock] = #newValueInt, [modifyDate] = GETDATE()
WHERE [inventoryID] = #updateInventoryID
END
IF('quantityOnHand' = #fieldName)
BEGIN
SET #newValueInt = CONVERT(int, #updateValue)
UPDATE [dbo].[myTable]
SET [quantityOnHand] = #newValueInt, [modifyDate] = GETDATE()
WHERE [inventoryID] = #updateInventoryID
END
IF('totalLeadTimeDays' = #fieldName)
BEGIN
SET #newValueInt = CONVERT(int, #updateValue)
UPDATE [dbo].[myTable]
SET [totalLeadTimeDays] = #newValueInt, [modifyDate] = GETDATE()
WHERE [inventoryID] = #updateInventoryID
END
IF('stockTakeETADate' = #fieldName)
BEGIN
SET #newValueDatetime = CONVERT(datetime, #updateValue)
UPDATE [dbo].[myTable]
SET [stockTakeETADate] = #newValueDatetime, [modifyDate] = GETDATE()
WHERE [inventoryID] = #updateInventoryID
END
COMMIT
A Try/Catch is advisable
I'm having this error when I try to execute this code:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
I know the problem is on the sp [dbo].[QueueInsert], the sp has an error and it goes directly to the catch. It's funny because the first one (EXEC [dbo].[BacthInsert]) is inserting a summary record and after running the sp, I see the record, so is doing a commit and not doing a rollback. What's wrong with this code? thanks!
CREATE PROCEDURE [cnfg].[SendEmail]
(
,#Employees [dbo].[Employees] readonly
,#ApplicationName NVARCHAR(256)
,#ErrorMsg NVARCHAR(300) = NULL OUTPUT
)
AS
BEGIN
DECLARE #ReturnVal INT
DECLARE #ApplicationId UNIQUEIDENTIFIER
DECLARE #NewIdBatch INT
DECLARE #ID INT
DECLARE #EmployeeId INT
DECLARE #Index INT = 1
DECLARE #Total INT
SET NOCOUNT ON;
SET XACT_ABORT ON;
SET #ReturnVal = 0;
SET #ErrorMsg = '';
SET #ApplicationId = [GetId](#ApplicationName);
IF (#ApplicationId IS NULL)
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'The Application Name does not exist in the database';
Goto ProcedureExit
END
----------------------------------------------------------
BEGIN TRY -- Start Main TRY
----------------------------------------------------------
BEGIN TRANSACTION;
EXEC [dbo].[BacthInsert]
#ParameterId = 1
,#ID = #NewSendEmailBatchId OUTPUT
,#ErrorMsg = #ErrorMsg OUTPUT
IF ( #ErrorMsg <> '' )
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'There was an error trying to insert data into [dbo].[BacthInsert] table';
RAISERROR(#ErrorMsg, 16, 1)
END
SELECT ROW_NUMBER() OVER ( ORDER BY EmployeeId ) Row,
EmployeeId
INTO #EmpIds
FROM #Employees
SELECT #Total = COUNT(*) FROM #EmpIds
WHILE ( #Index <= #Total )
BEGIN
SELECT #EmployeeId=EmployeeId FROM #EmpIds WHERE Row = #Index
EXEC [dbo].[QueueInsert]
#SendEmailBatchId = #NewIdBatch
,#ID = #ID OUTPUT
,#ErrorMsg = #ErrorMsg OUTPUT
IF ( #ErrorMsg <> '' )
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'There was an error trying to insert data into [dbo].[QueueInsert] table';
RAISERROR(#ErrorMsg, 16, 1)
END
SET #Index+=1;
END
COMMIT TRANSACTION;
----------------------------------------------------------
END TRY -- End Main TRY
----------------------------------------------------------
----------------------------------------------------------
BEGIN CATCH -- Start Main CATCH
----------------------------------------------------------
SELECT ERROR_MESSAGE()
IF (XACT_STATE()) = -1
BEGIN
ROLLBACK TRANSACTION;
END;
----------------------------------------------------------
END CATCH -- End Main CATCH
----------------------------------------------------------
ProcedureExit:
RETURN #ReturnVal;
END
I have a lot of stored procedures. But I am only getting Request Timeout sometimes only for this SP ?
ALTER PROCEDURE [dbo].[Insertorupdatedevicecatalog]
(#OS NVARCHAR(50)
,#UniqueID VARCHAR(500)
,#Longitude FLOAT
,#Latitude FLOAT
,#Culture VARCHAR(10)
,#Other NVARCHAR(200)
,#IPAddress VARCHAR(50)
,#NativeDeviceID VARCHAR(50))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TranCount INT;
SET #TranCount = ##TRANCOUNT;
DECLARE #OldUniqueID VARCHAR(500) = ''-1'';
SELECT #OldUniqueID = [UniqueID] FROM DeviceCatalog WHERE (#NativeDeviceID != '''' AND [NativeDeviceID] = #NativeDeviceID);
BEGIN TRY
IF #TranCount = 0
BEGIN TRANSACTION
ELSE
SAVE TRANSACTION Insertorupdatedevicecatalog;
DECLARE #Geo GEOGRAPHY = geography::STGeomFromText(''POINT('' + CONVERT(VARCHAR(100), #Longitude) + '' '' + CONVERT(VARCHAR(100), #Latitude) + '')'', 4326);
IF EXISTS(SELECT 1 FROM DeviceCatalog WHERE [UniqueID] = #UniqueID)
BEGIN
DECLARE #OldGeo GEOGRAPHY
,#OldCity NVARCHAR(100)
,#OldCountry NVARCHAR(100)
,#OldAddress NVARCHAR(100);
SELECT #OldGeo = [LastUpdatedLocationFromJob]
,#OldCity = [City]
,#OldCountry = [Country]
,#OldAddress = [Address]
FROM DeviceCatalog
WHERE [UniqueID] = #UniqueID;
UPDATE DeviceCatalog
SET [OS] = #OS
,[Location] = #Geo
,[Culture] = #Culture
,[Other] = #Other
,[IPAddress] = #IPAddress
WHERE [UniqueID] = #UniqueID;
IF (#OldGeo IS NULL OR #OldAddress IS NULL OR #OldCity IS NULL OR #OldCountry IS NULL OR ISNULL(#Geo.STDistance(#OldGeo) / 1000,0) > 50)
BEGIN
UPDATE DeviceCatalog
SET [Lastmodifieddate] = Getdate()
WHERE [UniqueID] = #UniqueID;
END
END
ELSE
BEGIN
INSERT INTO DeviceCatalog
([OS]
,[UniqueID]
,[Location]
,[Culture]
,[Other]
,[IPAddress]
,[NativeDeviceID])
VALUES (#OS
,#UniqueID
,#Geo
,#Culture
,#Other
,#IPAddress
,#NativeDeviceID);
IF(#OldUniqueID != ''-1'' AND #OldUniqueID != #UniqueID)
BEGIN
EXEC DeleteOldAndroidDeviceID #OldUniqueID, #UniqueID;
END
END
LBEXIT:
IF #TranCount = 0
COMMIT;
END TRY
BEGIN CATCH
DECLARE #Error INT, #Message VARCHAR(4000), #XState INT;
SELECT #Error = ERROR_NUMBER() ,#Message = ERROR_MESSAGE() ,#XState = XACT_STATE();
IF #XState = -1
ROLLBACK;
IF #XState = 1 AND #TranCount = 0
rollback
IF #XState = 1 AND #TranCount > 0
ROLLBACK TRANSACTION Insertorupdatedevicecatalog;
RAISERROR (''Insertorupdatedevicecatalog: %d: %s'', 16, 1, #error, #message) ;
END CATCH
END
The timeout occurs due to two updates to same table inside same transaction. You could avoid it with a case statement. Also whole IF ELSE can be replaced with a merge.
MERGE INTO DeviceCatalog DC
USING (SELECT #UniqueID AS UniqueID) T ON (DC.UniqueID = T.UniqueID)
WHEN MATCHED THEN
UPDATE SET [OS] = #OS
,[Location] = #Geo
,[Culture] = #Culture
,[Other] = #Other
,[IPAddress] = #IPAddress
,[Lastmodifieddate] = (CASE
WHEN (LastUpdatedLocationFromJob IS NULL OR [Address] IS NULL OR [City] IS NULL OR [Country] IS NULL OR ISNULL(#Geo.STDistance(LastUpdatedLocationFromJob) / 1000,0) > 50)
THEN Getdate()
ELSE [Lastmodifieddate]
END)
WHEN NOT MATCHED THEN
INSERT INTO DeviceCatalog
([OS]
,[UniqueID]
,[Location]
,[Culture]
,[Other]
,[IPAddress]
,[NativeDeviceID])
VALUES (#OS
,#UniqueID
,#Geo
,#Culture
,#Other
,#IPAddress
,#NativeDeviceID)
WHEN NOT MATCHED BY SOURCE AND #OldUniqueID != ''-1'' AND #OldUniqueID != #UniqueID THEN
DELETE;
Try it and check whether this is what you expected.
Already discussed here
You can achieve it using sp_getapplock in TSQL.
But you need a wrapper storedproc or batch for this. Check the following example it will help you to desing your wrapper sp/batch statement.
Sample Code Snippet
Create table MyTable
(
RowId int identity(1,1),
HitStartedAt datetime,
HitTimestamp datetime,
UserName varchar(100)
)
Go
Create proc LegacyProc (#user varchar(100), #CalledTime datetime)
as
Begin
Insert Into MyTable
Values(#CalledTime, getdate(), #user);
--To wait for 10 sec : not required for your procedures, producing the latency to check the concurrent users action
WAITFOR DELAY '000:00:10'
End
Go
Create Proc MyProc
(
#user varchar(100)
)
as
Begin
Declare #PorcName as NVarchar(1000), #CalledTime datetime
Begin Tran
--To get the Current SP Name, it should be unique for each SP / each batch
SET #PorcName = object_name(##ProcID)
SET #CalledTime = Getdate()
--Lock the Current Proc
Exec sp_getapplock #Resource = #PorcName, #LockMode = 'Exclusive'
--Execute Your Legacy Procedures
Exec LegacyProc #user, #CalledTime
--Release the lock
Exec sp_releaseapplock #Resource = #PorcName
Commit Tran
End
You are doing two seperate updates on the DeviceCatalog table where [UniqueID] = #UniqueID in the same transaction.
I bet your locking/request timeout issue is happening when:
IF (#OldGeo IS NULL OR #OldAddress IS NULL OR #OldCity IS NULL OR #OldCountry IS NULL OR ISNULL(#Geo.STDistance(#OldGeo) / 1000,0) > 50) is true.
Try something like this in place of the two updates.
Obviously test in dev first.
In the else clause, you want to have it insert something if the when is false. Here I am just inserting the current before update field contents.
UPDATE DeviceCatalog
SET [OS] = #OS
,[Location] = #Geo
,[Culture] = #Culture
,[Other] = #Other
,[IPAddress] = #IPAddress
,[Lastmodifieddate] =
case when (
#OldGeo is NULL
OR
#OldAddress is NULL
OR
#OldCity is NULL
OR
#OldCountry is NULL
OR
ISNULL(#Geo.STDistance(#OldGeo) / 1000,0) > 50
) then Getdate()
else [Lastmodifieddate]
end
WHERE [UniqueID] = #UniqueID
I've checked all the tables in question and I've declared all variables correctly.
the code below calls on a very basic stored procedure which can be provided if needed (I'm not sure how to add it in here properly)
DECLARE #DebtorsDebtID AS uniqueidentifier
DECLARE #OldClient AS varchar(15)
DECLARE #ClientID AS varchar(15)
DECLARE #UserName AS varchar(20)
DECLARE #Rows AS int
DECLARE #Count AS int
DECLARE #ID AS int
DECLARE #DebtID AS int
SET #UserName = 'rhys.bartley'
SET #OldClient = 'TMTEST'
SET #ClientID = 'ECCOMMERCIAL'
SELECT tblDebt.PK_DebtID INTO #tmp
FROM tblDebt
WHERE tblDebt.PK_DebtID = 233101
SELECT #Rows = ##ROWCOUNT, #Count = 0
WHILE (#Count < #Rows)
BEGIN
SELECT TOP 1 #ID = PK_DebtID FROM #tmp
SELECT #DebtorsDebtID = PK_DebtorsDebtID FROM tblDebtorsDebt where FK_DebtID = #ID
EXEC sp_DRPL_MassChangeClient #DebtorsDebtID,#OldClient,#ClientID,#UserName,#DebtID
DELETE #tmp WHERE PK_DebtID = #ID
SELECT #Count = #Count + 1
END
sp_DRPL_MassChangeClient
USE [BailiffDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_DRPL_MassChangeClient]
#DebtorsDebtID AS uniqueidentifier,
#DebtID AS int,
#OldClient AS varchar(15),
#ClientID AS varchar(15),
#UserName AS varchar(20)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #BatchNo int
SET #BatchNo = 2457
UPDATE tblArrangement SET FK_ClientID = #ClientID WHERE FK_DebtorsDebtID = #DebtorsDebtID
UPDATE tblDebt SET FK_ClientID = #ClientID WHERE PK_DebtID = #DebtID
UPDATE tblDebtorsDebt SET FK_ClientID = #ClientID WHERE PK_DebtorsDebtID = #DebtorsDebtID
UPDATE tblLetterActivity SET FK_ClientID = #ClientID WHERE FK_DebtID = #DebtID
UPDATE tblTransactions SET FK_ClientID = #ClientID WHERE FK_DebtID = #DebtID
UPDATE tblTransactionsDistributed SET FK_ClientID = #ClientID WHERE FK_DebtID = #DebtID
UPDATE tblTransactionsDistributed_Cancelled SET FK_ClientID = #ClientID WHERE FK_DebtID = #DebtID
UPDATE tblTransactions_Cancelled SET FK_ClientID = #ClientID WHERE FK_DebtID = #DebtID
UPDATE tblDebtLoad SET FK_ClientID = #ClientID WHERE PK_DebtID = #DebtID
UPDATE tblBatchNo SET FK_ClientID = #ClientID WHERE PK_BatchNo = #BatchNo
END
This is what your passing
EXEC sp_DRPL_MassChangeClient #DebtorsDebtID,#OldClient,#ClientID,#UserName,#DebtID
the second #variable is #OldClient which is declared as varchar into
#DebtorsDebtID AS uniqueidentifier,
#DebtID AS int,
The second variable is #DebtID declared as INT hence the error thrown
I create a stored procedure in which I call two functions .
I'm getting an error:
Msg 4104, Level 16, State 1, Procedure Add_Translation, Line 25
The multi-part identifier ".word" could not be bound.
This is my stored procedure:
ALTER PROCEDURE [dbo].[Add_Translation]
#english nvarchar(70),
#kurdish nvarchar(70),
#english_category int,
#kurdish_category int,
#result int out
AS
BEGIN
SET NOCOUNT ON;
if #english is not null and #kurdish is not null and #english_category is not null and #kurdish_category is not null
begin
declare #intEnId int=-1;
exec #intEnId = Check_English_word #text = #english;
declare #identityEnglish int;
declare #identityKurdish int;
if #intEnId = -1
begin
insert into english_table values(#english, #english_category);
set #identityEnglish = SCOPE_IDENTITY();
end
else
begin
set #identityEnglish = (select e.english_id from english_table e where UPPER(.word)=UPPER(#english));
end
declare #intKuId int=-1;
exec #intKuId=Check_Kurdish_Word #word=#kurdish;
if #intKuId =-1
begin
insert into kurdish_table values(#kurdish, #kurdish_category);
set #identityKurdish = SCOPE_IDENTITY();
end
else
begin
set #identityKurdish = (select k.kurdish_id from kurdish_table k where upper(k.word)=upper(#kurdish));
end
declare #translated int =0;
exec #translated = Check_Translation #english_id = #identityEnglish, #kurdish_id = #identityKurdish;
if #translated=0
begin
insert into transactions values(#identityEnglish, #identityKurdish);
set #result = 1;
end
else
begin
set #result = 2;
end
end
else
begin
set #result = 0;
end
END
Here is the first function:
ALTER FUNCTION [dbo].[Check_English_word]
(
#text nvarchar(70)
)
RETURNS int
AS
BEGIN
DECLARE #Result int
set #Result=-1;
if #text is not null
begin
SELECT #Result = e.english_id
from english_table e
where UPPER(e.word) = UPPER(#text);
end
RETURN #Result
END
Second function:
ALTER FUNCTION [dbo].[Check_Kurdish_Word]
(
#word nvarchar(70)
)
RETURNS int
AS
BEGIN
DECLARE #Result int
set #Result=-1;
if #word is not null
begin
SELECT #Result = k.kurdish_id
from kurdish_table k
where UPPER(k.word) = UPPER(#word);
end
RETURN #Result
END
You are missing an e in this line
set #identityEnglish=(select e.english_id from english_table e
where UPPER(.word)=UPPER(#english));
Change it to
set #identityEnglish=(select e.english_id from english_table e
where UPPER(e.word)=UPPER(#english));
Also, it is good practice to specify columns when doing an insert -
ie
insert into english_table values(#english,#englsih_category);
should be
insert into english_table (word, category) values(#english,#englsih_category);