Sql Stored Procedure to insert and update - sql

I am new to Stored Procedures and SQL. Looking in to various articles, I found how to insert an record using stored procedure and it works.
CREATE PROCEDURE [dbo].[stprOrder]
#OrderDate date,
#OrderID nchar(50),
#ShipToID nchar(50),
#TotalAmt decimal(18,2),
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO ORDER(OrderDate,OrderID,ShipToID,TotalAmt)
Values(#OrderDate,#OrderID,#ShipToID,#TotalAmt)
END
I am not sure how to update an record using the same stprOrder stored procedure. Like the stored procedure should do inserting and updating depending on the OrderID.

Most likely you're looking for something like this
CREATE PROCEDURE [dbo].[stprOrder]
#OrderDate date,
#OrderID nchar(50),
#ShipToID nchar(50),
#TotalAmt decimal(18,2),
AS
BEGIN
SET NOCOUNT ON;
IF (SELECT TOP (1) 1 FROM ORDER WHERE OrderID = #OrderID) IS NULL
INSERT INTO ORDER(OrderDate,OrderID,ShipToID,TotalAmt)
Values(#OrderDate,#OrderID,#ShipToID,#TotalAmt)
ELSE
UPDATE ORDER SET OrderDate = #OrderDate, ShipToID = #ShipToID, TotalAmt = #TotalAmt
WHERE OrderID = #OrderID
END
First it checks if order with given ID already exists - if it doesn't - a new entry is created, otherwise existing record is updated

CREATE PROCEDURE [dbo].[stprOrder]
#OrderDate date,
#OrderID nchar(50),
#ShipToID nchar(50),
#TotalAmt decimal(18,2),
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT null FROM ORDER WHERE id = #orderID)
BEGIN
UPDATE ORDER SET ..... WHERE id = #orderID
END
ELSE
BEGIN
INSERT INTO ORDER(OrderDate,OrderID,ShipToID,TotalAmt)
VALUES(#OrderDate,#OrderID,#ShipToID,#TotalAmt)
END

Related

SQL: Set variable to a cell value inside a stored procedure

I am trying to store a single cell value from one table in a variable (inside a stored procedure), so I can use it to edit a value in another table, but I keep getting a MSG 201:
Procedure or function 'spBookReturn' expects parameter '#bookID', which was not supplied.
Every time I try to run the sp where it all should happen:
CREATE PROC spBookReturn
#loanID UNIQUEIDENTIFIER,
#bookID UNIQUEIDENTIFIER OUTPUT
AS
BEGIN
BEGIN TRANSACTION tBookReturn
UPDATE BorrowedMaterial SET returned = 1, returnedDate = GETDATE();
SET #bookID = (SELECT TOP 1 bookID FROM BorrowedMaterial WHERE loanID = #loanID ORBER BY returnedDate);
UPDATE Books SET nHome = nHome + 1 WHERE ID = #bookID;
COMMIT TRANSACTION tBookReturn;
END;
EXEC spBookReturn '546A444A-3D8D-412E-876D-2053D575B54F'
Does anyone know why the way I have defined the #bookID variable doesn't work and how I can make it work?
Thanks.
EDIT:
I got two tables: BorrowedMaterial that includes the attributes loanID, bookID, returned, returnedDate and a few others that's not relevant.
The other table is Books and it includes bookID, nHome but not the loanID.
So by giving only the loanID as an input, I would like to update the nHome. I am trying to grab bookID since it is the only thing the two attributes got in common and this is where the issues happen.
Side note: I removed the variable #custID it spawned by mistake.
All parameters for a procedure are Input parameters. If you declare a parameter as an OUTPUT parameter, it is still an input one, and if it doesn't have a default value must be supplied.
If you want the OUTPUT parameters to be option, which I personally find can be quite often, then give them a default value. I also add some additional logic to your procedure, as you should be using an TRY...CATCH and an ORDER BY in your query with a TOP.
CREATE PROC dbo.spBookReturn #loanID uniqueidentifier,
#bookID uniqueidentifier = NULL OUTPUT,
#custID uniqueidentifier = NULL OUTPUT
AS
BEGIN
BEGIN TRY --If you are using tranasctions, make sure you have a ROLLBACK and THROW for errors
BEGIN TRANSACTION tBookReturn
UPDATE BorrowedMaterial
SET returned = 1,
returnedDate = GETDATE()
WHERE loanID = #loanID;
/*
UPDATE BorrowedMaterial
SET returnedDate = GETDATE()
WHERE loanID = #loanID;
*/
SET #bookID = (SELECT TOP 1 bookID
FROM BorrowedMaterial
WHERE loanID = #loanID
ORDER BY ???); --A TOP should have an ORDER BY
UPDATE Books
SET nHome = nHome + 1
WHERE ID = #bookID;
COMMIT TRANSACTION tBookReturn;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION tBookReturn;
THROW;
END CATCH;
END;
Then you can execute the procedure as you have, without #bookID and #custID being passed. Of course, if you don't, their values will be "lost" in the calling statement. If you need them, then pass their values too in the EXEC:
DECLARE #bookID uniqueidentifier, #CustID uniqueidentifier;
EXEC dbo.spBookReturn #loanID, #bookID OUTPUT, #CustID OUTPUT;
--SELECT #bookID, #CustID;
declare #bookID UNIQUEIDENTIFIER;
declare #custID UNIQUEIDENTIFIER;
declare #InResult;
exec #InResult= spBookReturn '546A444A-3D8D-412E-876D-2053D575B54F',#bookID,#custID;
select #bookID,#custID;
Seems, you need something like this
My take on what you're looking for. Because there are multiple DML statements (2 Updates) in a single transaction the XACT_ABORT ON option ensures a complete rollback. Also, the THROW in the CATCH block is placed before the ROLLBACK to preserve the SQL generated error metadata. Prior to executing the proc the OUTPUT variables are declared and placed in the parameter list (this omission is what was causing the initial error).
drop proc if exists dbo.spBookReturn;
go
CREATE PROC dbo.spBookReturn
#loanID uniqueidentifier,
#bookID uniqueidentifier OUTPUT,
#custID uniqueidentifier OUTPUT
AS
set nocount on;
set xact_abort on;
BEGIN TRANSACTION tBookReturn
BEGIN TRY
declare #ins_bm table(book_id uniqueidentifier,
cust_id uniqueidentifier);
UPDATE BorrowedMaterial
SET returned = 1,
returnedDate = GETDATE()
output inserted.book_id, inserted.cust_id into #ins_bm
WHERE loanID = #loanID;
if ##rowcount=0
begin
select #bookID=0,
#custID=0;
throw 50000, 'No update performed', 1;
end
else
begin
UPDATE b
SET nHome = nHome + 1
from Books b
WHERE exists (select 1
from #ins_bm bm
where b.ID = bm.book_id);
select top(1) #bookID=book_id,
#custID=cust_id
from #ins_bm;
end
COMMIT TRANSACTION tBookReturn;
END TRY
BEGIN CATCH
THROW
ROLLBACK TRANSACTION tBookReturn;
END CATCH;
go
declare #bookID UNIQUEIDENTIFIER;
declare #custID UNIQUEIDENTIFIER;
declare #InResult int;
exec #InResult=spBookReturn '546A444A-3D8D-412E-876D-2053D575B54F', #bookID output, #custID output;
select #bookID, #custID;
Thanks to #Larnu I figured out the only thing missing was a = on line 3 (and thanks to #charlieface I also got my code cleaned up a tiny bit):
CREATE PROC spBookReturn
#loanID UNIQUEIDENTIFIER,
#bookID UNIQUEIDENTIFIER = NULL OUTPUT
AS
BEGIN
BEGIN TRANSACTION tBookReturn
UPDATE BorrowedMaterial SET returned = 1, returnedDate = GETDATE();
SET #bookID = (SELECT TOP 1 bookID FROM BorrowedMaterial WHERE loanID = #loanID
ORDER BY returnedDate);
UPDATE Books SET nHome = nHome + 1 WHERE ID = #bookID;
COMMIT TRANSACTION tBookReturn;
END;
EXEC spBookReturn '546A444A-3D8D-412E-876D-2053D575B54F'

Stored procedure to search Table based on multiple values from dropdown

I have three down with different values such as
Category One dropdown
Category Two dropdown
Year dropdown
I Have to search Product table based on dropdown values it should filter search based on any one of the dropdown or all of the dropdown selected values.
Let us say i have following fields in Product Table
ProductID
ProductName
ProductCatOne
ProductCatTwo
Description
Image
....
....
How is the most efficient way to write a store procedure so that i cant handle any of the three value selected. I want to avoid two many if statements in store procedure
ALTER PROCEDURE [dbo].[sp_SearchProduct]
#ProductID int,
#ProductCatOne int,
#ProductCatTwo int
AS
BEGIN
SET NOCOUNT ON;
If #ProductID > 0 THEN
END IF
END
STORED PROCEDURE FOR MS SQL SERVER
I am not sure how to create dynamic query inside SP fr this search
It is called Dynamic Search Conditions. I recommend you to read this excellent article by Erland Sommarskog. He explains several ways to do it and why OPTION(RECOMPILE) is needed if you don't use dynamic SQL as in the example below.
Few notes.
It is bad practice to name your stored procedures with the prefix sp_.
I prefer to pass NULL value instead of 0 to indicate that this parameter should be ignored. 0 value can be a valid value for search.
ALTER PROCEDURE [dbo].[SearchProduct]
#ProductID int,
#ProductCatOne int,
#ProductCatTwo int
AS
BEGIN
SET NOCOUNT ON;
SELECT
...
FROM Products
WHERE
(ID = #ProductID OR #ProductID IS NULL)
AND (ProductCatOne = #ProductCatOne OR #ProductCatOne IS NULL)
AND (ProductCatTwo = #ProductCatTwo OR #ProductCatTwo IS NULL)
OPTION(RECOMPILE);
END
This code assumes that columns ID, ProductCatOne, ProductCatTwo can't have NULLs.
ALTER PROCEDURE [dbo].[sp_SearchProduct]
#ProductID int,
#ProductCatOne int,
#ProductCatTwo int
AS
BEGIN
SET NOCOUNT ON;
IF #ProductID =''
SET #ProductID=NULL
IF #ProductCatOne =''
SET #ProductCatOne=NULL
IF #ProductCatTwo =''
SET #ProductCatTwo=NULL
SELECT *
FROM Product
WHERE ID = COALESCE (#ProductID,ID)
AND ProductCatOne =COALESCE (#ProductID,ProductCatOne )
AND ProductCatTwo=COALESCE (#ProductID,ProductCatTwo)
END
ALTER PROCEDURE [dbo].[SearchProduct]
#ProductID int,
#ProductName int,
#ProductCatOne int
AS
BEGIN
SET NOCOUNT ON;
SELECT
...
FROM Products
WHERE
(Case When #ProductID <> 'ALL' Then ProductID Else #ProductID End ) in(#ProductID) And
(Case When #ProductName <> 'ALL' Then ProductName Else #ProductName End ) in(#ProductName) And
(Case When #ProductCatOne <> 'ALL' Then ProductCatOne Else #ProductCatOne End ) in(#ProductCatOne)
END

Stored procedure unsure syntax

I am trying to create a stored procedure that will determine if my customerid exists if it exists then my other parameter foundcustomer will be assigned to found otherwise not found. I am unsure how to assign found please help
here is what i tried
CREATE PROCEDURE procedure4
-- Add the parameters for the stored procedure here
#FoundCustomer varchar(10) = null,
#Customerid varchar (5) = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
if Not(#Customerid is null)
SELECT customerid
from customers
where customerid = #Customerid
END
GO
Gordon is right, it sounds like you may want a function but if it has to be a stored procedure you can follow this example.
CREATE PROCEDURE procedure4
#Customerid varchar(5) = null
AS
BEGIN
SET NOCOUNT ON;
DECLARE #FoundCustomer varchar(10) = ''
IF #FoundCustomer is not null
BEGIN
IF (SELECT COUNT(1) FROM customers WHERE customerid = #customerid) > 0
SET #FoundCustomer = 'Found'
ELSE
SET #FoundCustomer = 'Not Found'
END
SELECT #FoundCustomer
END

SQL Stored Procedure not handling error from nested stored procedure

I have two stored procedures, one nested inside the other. When the nested stored procedure is called, at the moment, it should error with a foreign key constraint violation and then rollback the earlier call to insert into the ProductLicense table. The nested procedure does not perform any action on the database because of the foreign key violation but the calling stored procedure isn't catching the error and rolling back. If I execute the nested stored procedure by itself it does return error 547 Foreign key violation.
How can I get the two stored procedures to work together?
Outer procedure:
ALTER PROCEDURE [dbo].[AddNewLicense2_i]
-- Add the parameters for the stored procedure here
#customerId nvarchar(10),
#licenseModeId int,
#licenseModeProgramId int,
#createdBy int,
#updateBy int,
#systemId nvarchar(50),
#productId int
AS
BEGIN TRY
BEGIN TRANSACTION
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--SET XACT_ABORT ON; --used for automatic rollback when an error occurs
DECLARE #tempDays INT
DECLARE #programCornerAmt INT
DECLARE #tempEndDate DATETIME
DECLARE #tempExpDate DATETIME
DECLARE #err INT
SET #err = 0
/*SET #tempDays = (SELECT lmp.TimeoutDays
FROM LicenseModeProgram lmp
WHERE lmp.LicenseModeProgramId = #licenseModeProgramId)*/
SELECT #tempDays = TimeoutDays, #programCornerAmt = MonthlyCornersAmount
FROM LicenseModeProgram
WHERE LicenseModeProgramId = #licenseModeProgramId
--Build Expiration and End Dates.
IF #tempDays = NULL --then this is NOT a time rental or metered system
BEGIN
SET #tempEndDate = NULL
SET #tempExpDate = NULL
END
ELSE
BEGIN
SET #tempEndDate = DATEADD("d", #tempDays, GETDATE())
SET #tempExpDate = DATEADD("d", #tempDays, GETDATE())
END
-- Create new product license record
INSERT INTO ProductLicense (CustomerId, LicenseModeId, LicenseModeProgramId, CreatedBy, UpdatedBy, SystemId, ProductId, ExpirationDate, LicenseEndDate)
VALUES (#customerId, #licenseModeId, #licenseModeProgramId, #createdBy, #updateBy, #systemId, #productId, #tempExpDate, #tempEndDate)
IF #licenseModeId = 4 AND #systemId NULL AND #programCornerAmt NULL
--call stored procedure to add corners to the customer account
EXECUTE #err = AddMeteredTx_i #systemId, 1, 1, #programCornerAmt , 'Initial License Creation'
PRINT #err
COMMIT TRANSACTION
END TRY
BEGIN CATCH
RAISERROR('Failed to Create License', 11, 2)
ROLLBACK TRANSACTION
RETURN 1
END CATCH
--COMMIT TRANSACTION
RETURN 0
GO
Inner procedure:
ALTER PROCEDURE [dbo].[AddMeteredTx_i]
-- Add the parameters for the stored procedure here
#systemId nvarchar(50),
#activityEventId int,
#createdBy int,
#amount int,
#notes text
AS
BEGIN TRY
BEGIN TRANSACTION
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--SET XACT_ABORT ON; --used for automatic rollback when an error occurs
INSERT INTO CustomerAccountActivity (SystemId, ActivityEventId, CreatedBy, Amount, Notes)
VALUES (#systemId, #activityEventId, #createdBy, #amount, #notes)
UPDATE CustomerAccount
SET MeteredBalance = (SELECT MeteredBalance FROM CustomerAccount WHERE SystemId = #systemId) + #amount
WHERE SystemId = #systemId
COMMIT TRANSACTION
END TRY
BEGIN CATCH
RAISERROR('Error Update to Customer Account Record ', 11, 2)
ROLLBACK TRANSACTION
RETURN 1
--COMMIT TRANSACTION
END CATCH
RETURN 0
GO
Catching errors with a call stack like this using ##Error can be problematic. It's a lot more reliable to use TRY/CATCH
The basic format is:
BEGIN TRY
<BEGIN TRAN>
... do stuff ...
<COMMIT TRAN>
END TRY
BEGIN CATCH
<ROLLBACK TRAN>
... do error stuff like re-raise the error to outer scope ...
END CATCH
Any error encountered in the try will automatically take you to the CATCH block without additional checking.

Storedprocedure to check the value already in the table

I have a stored procedure to insert values in to a table.Here I need to check the values for insert is already in the table .how can I check this in my stored procedure.here is my stored procedure.
USE [Databasse_sync]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS
INSERT INTO YoutubeVideo(ProductID,YoutubeUrl,YoutubeImage,DATASET)VALUES(#ProductID,#YoutubeUrl,#YoutubeImage,'DAT')
RETURN
Here I need to check the ProducId is same or not?If ProductId is same then Update otherwise Insert.>>>??
USE [Databasse_sync]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS
MERGE [YoutubeVideo] AS Y
USING (SELECT #ProductID,#YoutubeUrl, #YoutubeImage) AS SourceRow (ProductID,YoutubeUrl,YoutubeImage)
ON Y.[ProductID] = SourceRow.ProductID
WHEN MATCHED THEN
UPDATE SET
Y.[ProductID] = SourceRow.ProductID
,Y.[YoutubeUrl] = SourceRow.YoutubeUrl
,Y.[YoutubeImage] = SourceRow.YoutubeImage
WHEN NOT MATCHED THEN
INSERT (ProductID,YoutubeUrl, YoutubeImage, DATASET)
VALUES (SourceRow.ProductID,SourceRow.YoutubeUrl, SourceRow.YoutubeImage,'DAT');
RETURN
IF NOT EXISTS(SELECT TOP 1 ProductID FROM YoutubeVideo WHERE ProductID=#ProductID) BEGIN
--INSERT HERE
END
ELSE BEGIN
--UPDATE HERE
END
DECLARE #EXISTS BIT
SELECT #EXISTS = 1
WHERE EXISTS
(SELECT ID FROM YouTubeVideo
WHERE ID = #ProductID
AND YoutubeUrl = #YoutubeUrl)
IF #Exists = 1
BEGIN
-- Update
END
ELSE
BEGIN
-- INSERT
END
#Arun
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS
BEGIN
DECLARE #counter INT
#counter=SELECT COUNT(ProductID) FROM YoutubeVideo WHERE ProductID=#ProductID
IF(#counter = 0)
BEGIN
INSERT INTO YoutubeVideo(ProductID,YoutubeUrl,YoutubeImage,DATASET)VALUES(#ProductID,#YoutubeUrl,#YoutubeImage,'DAT')
END
END
if u want to check record is inserted or not then u can take one out parameter and chek recored is inserted or not.
try this:
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS BEGIN
DECLARE #counter int
#counter = SELECT count(ProductID) FROM YoutubeVideo WHERE ProductID=#ProductID
IF(#counter = 0)
BEGIN
INSERT INTO YoutubeVideo(ProductID,YoutubeUrl,YoutubeImage,DATASET)VALUES(#ProductID,#YoutubeUrl,#YoutubeImage,'DAT')
End
ELSE
begin
UPDATE SET
ProductID = #ProductID
,YoutubeUrl = #YoutubeUrl
,YoutubeImage = #YoutubeImage
END END