Whats Wrong with the Stored Procedure below? - sql-server

alter procedure product_sales
#productid int,
#quantitysell int
as
begin
declare #productname varchar(20)
declare #quantityavailable int
select #quantityavailable=quantityav from products_trycatch
select #productname=productname from products_trycatch where productid=#productid
select #productid=productid from products_trycatch
if(not exists(select #productid from products_trycatch)
begin
raiserror('Product does not exist',16,1)
end
else
begin
if (#quantitysell>#quantityavailable)
begin
print 'Stock not available'
end
else
begin
-------------
update products_trycatch set quantityav=quantityav-#quantitysell
insert into product_log values(#productid,#productname,#quantitysell)
end
Please let me know where the mistake is. What i am trying to do is. one table contains stock available for a product, when i execute the SP i mentioned which product and how many quantity is being sold, The sp deducts that value from the available quantity table and updates in a new table the quantity sold.

You're thinking very procedurally, laying things out step by step, but missing parts out from several of your steps.
Don't write several queries to retrieve individual pieces of data. Think about the problem as a whole1:
alter procedure product_sales
#productid int,
#quantitysell int
as
begin
declare #rc int
update products_trycatch
set quantityav = quantityav - #quantitysell
where productid = #productid and quantityav > #quantitysell
set #rc = ##ROWCOUNT
if #rc = 1
begin
insert into product_log
select #productid,productname,#quantitysell
from product_trycatch
where productid = #productid
end
else
begin
if not exists(select * from products_trycatch where productid = #productid)
begin
raiserror('Product does not exist',16,1)
end
else
begin
print 'Stock not available'
end
end
Not how all of my queries target the row in product_trycatch with the matching productid column - several of yours don't which means that they're going to be assigning non-deterministic values to your variables, or updating all rows in product_trycatch and deducting the #quantitysell value.
1For bonus points we could write a single update with case and an output clause that would eliminate the need to perform any re-querying of the product_trycatch table for the product name or in the failure path, but it's probably not worth the additional complexity here. This query shows the general technique:
declare #products table (ID int not null, Quantity int not null, Name varchar(20) not null)
insert into #products (ID,Quantity,Name) values (1,15,'Fred')
declare #ID int
declare #Qty int
declare #RefData table (OldQuantity int not null, NewQuantity int not null,
Name varchar(20) not null)
select #ID = 1, #Qty = 20
update #products
set Quantity = CASE WHEN Quantity >= #Qty THEN Quantity - #Qty ELSE Quantity END
output deleted.Quantity,inserted.Quantity, inserted.Name into #RefData
where ID = #ID
select * from #RefData
where you can play with the #ID and #Qty values to see the various outcomes reflected in #RefData when the quantity requested is higher than that which is available or where the product does or doesn't exist.

Formatted your code and added missing words -
alter procedure product_sales
#productid int,
#quantitysell int
as
begin
declare #productname varchar(20)
declare #quantityavailable int
select #quantityavailable=quantityav from products_trycatch
select #productname=productname from products_trycatch where productid=#productid
select #productid=productid from products_trycatch
if not exists(select #productid from products_trycatch)
begin
raiserror('Product does not exist',16,1)
end
else if (#quantitysell>#quantityavailable)
begin
print 'Stock not available'
end
else
begin
-------------
update products_trycatch set quantityav=quantityav-#quantitysell
insert into product_log values(#productid,#productname,#quantitysell)
end
end

Here is the Formatted code
alter procedure product_sales
#productid int,
#quantitysell int
as
begin
declare #productname varchar(20)
declare #quantityavailable int
select #quantityavailable=quantityav from products_trycatch
select #productname=productname from products_trycatch where productid=#productid
select #productid=productid from products_trycatch
if(not exists(select #productid from products_trycatch))
begin
raiserror('Product does not exist',16,1)
end
else if (#quantitysell>#quantityavailable)
begin
print 'Stock not available'
end
else
begin
-------------
update products_trycatch set quantityav=quantityav-#quantitysell
insert into product_log values(#productid,#productname,#quantitysell)
end
end

Just Modified your Stored Proc have a look at it
ALTER PROCEDURE Product_sales
(#Productid INT,
#Quantitysell INT
)
AS
BEGIN TRY
DECLARE #Productname VARCHAR(20)
DECLARE #Quantityavailable INT
SELECT #Quantityavailable = Quantityav
FROM Products_trycatch
SELECT #Productname = Productname
FROM Products_trycatch
WHERE Productid = #Productid
SELECT #Productid = Productid
FROM Products_trycatch
IF( #Productid IS NULL )
BEGIN
RAISERROR('Product does not exist',16,1)
END
ELSE IF ( #Quantitysell > #Quantityavailable )
BEGIN
PRINT 'Stock not available'
END
ELSE
BEGIN
DECLARE #Is_transcount INT
BEGIN TRANSACTION
SET #Is_transcount=1
UPDATE Products_trycatch
SET Quantityav = Quantityav - #Quantitysell;
INSERT INTO Product_log
VALUES (#Productid,
#Productname,
#Quantitysell)
COMMIT TRANSACTION
SET #Is_transcount=0
END
END TRY
BEGIN CATCH
IF( ##Trancount > 0 )
ROLLBACK TRANSACTION
DECLARE #Error_message VARCHAR(Max),
#Error_number INT,
#Error_procedure VARCHAR(100)
SELECT #Error_message = ERROR_MESSAGE(),
#Error_number = ERROR_NUMBER(),
#Error_procedure = ERROR_PROCEDURE()
END CATCH

The SP is open to a Parameter Sniffing issue.
You re-assign the value of #productid part way through your code.
From this point forward SQL will be making more assumptions about what plan to use because it no longer knows the actual value.

Related

T-SQL 2008- Exit when Value is NULL

I am looking for a way to exit an T-SQL script when #Value is null. This is what I have so far but it does not work as expected:
SELECT
#Value,
CASE
WHEN #Value IS NULL
RAISERROR('EXIT', 16, 1)
FROM
table
WHERE
name LIKE 'test'
Perhaps this will work for you:
DECLARE #Value INT = 1
IF( #Value IS NULL)
BEGIN
RAISERROR('Exit',16,1)
END
ELSE
BEGIN
SELECT #Value
END
IF #Value IS NULL RAISERROR('EXIT', 16,1);
Using a cursor and a temp table you can get the output you want. don't know if that's the goal,
USE AdventureWorksLT2012
DECLARE #CustomerID AS INT
DECLARE #CompanyName AS VARCHAR(MAX)
DECLARE #EmailAddress AS VARCHAR(MAX)
CREATE TABLE #output (CustomerID INT,CompanyName VARCHAR(MAX),EmailAddress VARCHAR(MAX))
DECLARE testCursor CURSOR
FOR
SELECT TOP (100)
CustomerID
,CompanyName
,EmailAddress
FROM SalesLT.Customer
ORDER BY customerID DESC;
OPEN testCursor;
FETCH NEXT FROM testCursor
INTO #CustomerID, #CompanyName, #emailAddress;
if #EmailAddress is not null
BEGIN
INSERT INTO #output values( #CustomerID, #CompanyName, #emailAddress);
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM testCursor
INTO #CustomerID, #CompanyName, #emailAddress;
if #EmailAddress is null
BEGIN
RAISERROR('Exit',16,1);
BREAK;
end
INSERT INTO #output values( #CustomerID, #CompanyName, #emailAddress);
END;
END
CLOSE testCursor;
DEALLOCATE testCursor;
SELECT * FROM #output;
DROP TABLE #output

SQL Server procedure error - duplicate entries inserted into the table

I am writing a procedure in SQL Server to insert or update records.
The update part of the code is working fine but when I am executing it for inserting, duplicate entries are inserted into the table.
I created the primary key to avoid this error but after creating that I am not able to insert any single record.
Here is the code :
Alter Procedure test_case
#id int,
#name nvarchar(20)
AS
If exists (Select t_id from testing2 where t_id = #id)
begin
update testing2
set t_id = #id, t_name = #name
where t_id = #id
end
else
begin
insert into testing2 (t_id, t_name, last_date, hard)
select
#id, #name, convert(date, getdate()), 'null'
from test
end
On executing it is showing 2 rows affected
You do not require test table in the select query
insert into testing2 (t_id, t_name, last_date, hard)
select
#id as t_id, #name as t_name, convert(date, getdate()) as last_date, 'null' as hard
is enough
I like to break functionality into smaller parts because it helps me to manage code better.
Maybe this is not a good example since it is pretty simple but I will write it anyway.
Create Procedure Testing2_InsertData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
Insert Into testing2
(t_id, t_name, last_date, hard)
Values
( #id, #name, GetDate(), null )
Go
Create Procedure Testing2_UpdateData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
Update testing2 Set
t_name = #name --, maybe last_date = GetDate()
Where ( t_id = #id )
Go
Create Procedure Testing2_SaveData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
If ( Exists( Select t_id From testing2 Where ( t_id = #id ) ) )
Exec Testing2_UpdateData #id, #name
Else
Exec Testing2_InsertData #id, #name
Go

Instead of insert trigger SQL

I am trying to create an 'instead of insert trigger' that will not let the name 'john' insert anything into a table. My problem is that even if i change the name to something else, the query is successful but the values arent added.
Any help would be appreciated, thanks in advance.
CREATE TRIGGER InsteadOfTrigger
ON Question4
INSTEAD OF INSERT
AS
Declare #name varchar(50)
Declare #question varchar(50)
Declare #Answer char
Set #name = 'John'
IF (select Username from inserted) = #name
BEGIN
RAISERROR ('You have not paid up your fee', 10,1)
ROLLBACK TRANSACTION
END
ELSE
BEGIN
INSERT INTO question4
values (#name, #question, #Answer)
END
Ok So I have removed your BEGIN and END statements between your IF ELSE statement and wrapped the trigger logic within a BEGIN END
As mentioned in the comments below you dont need the ROLLBACK TRANSACTION
Also you will need to populate #question and #Answer for those to be of any use.
CREATE TRIGGER InsteadOfTrigger
ON Question4
INSTEAD OF INSERT
AS
BEGIN
Declare #name varchar(50)
Declare #question varchar(50)
Declare #Answer char
Set #name = 'John'
IF (select Username from inserted) = #name
RAISERROR ('You have not paid up your fee', 10,1)
--ROLLBACK TRANSACTION
ELSE
INSERT INTO question4
values (#name, #question, #Answer)
END
Hmm, I notice you have declared, but not actually set a value for your variables in your else statement this may have caused SQL to not insert what you expected.
Strangely enough I'm required to do the same in an assignment at the moment, Here's my solution:
CREATE TRIGGER instead_of_insert
ON Question4
INSTEAD OF INSERT AS
Declare #Username varchar(25)
Declare #Question varchar(6)
Declare #Answer char(1)
Set #Username ='John'
IF (Select UserName from inserted) = #UserName
Begin
RAISERROR ('You have not paid up your fee', 10,1)
End
Else
Begin
Set #Username = (Select UserName from inserted)
Set #Question = (Select Question_ID from inserted)
Set #Answer = (Select Answer from inserted)
Insert into User_Responses
Values
(#username, #Question, #Answer)
End

Conversion failed in cursor in stored procedure

Input like 111111 and 101,102,103,104
I want to check whether user has access on this requests or not...
I tried a cursor as shown, but I get this error:
Conversion failed when converting the varchar value '101,102,103,104' to data type int.
Code:
ALTER PROCEDURE [dbo].[ValidateRqstId]
#UserID VARCHAR(50),
#RsqtIDs VARCHAR(300)
AS
BEGIN
Declare #RqstId int
Declare #Result int
Declare #UserIDToCheck VARCHAR(50)
Declare #RqstUserVal cursor for
Select RequestId
from REQUEST_LIST
where RequestId in (#RsqtIDs)
BEGIN
OPEN RqstUserVal
FETCH NEXT from RqstUserVal into #RqstId
WHILE(##fetch_status <> -1)
BEGIN
SET #UserIDToCheck = (
select UserId from dbo.REQUEST_LIST where RequestId = #RqstId)
Print(#UserIDToCheck)
If(#UserIDToCheck != #UserID)
SET #Result = 99 ;
--Fetch the next row from the cursor
FETCH RqstUserVal into #RqstId
END
END
CLOSE RqstUserVal
Deallocate RqstUserVal
RETURN #Result
END
Thanks in advance
Depending on your SQL-Server Verion you can use a Table-Valued Function like in this short example
Select * from dbo.test1
Where ID in(
Select * from dbo.F_SplitAsIntTable('1,123,234,456'))
Function defined as
CREATE FUNCTION F_SplitAsIntTable
(
#txt varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
declare #i int
declare #s varchar(20)
Set #i = CHARINDEX(',',#txt)
While #i>1
begin
set #s = LEFT(#txt,#i-1)
insert into #tab (id) values (#s)
Set #txt=RIGHT(#txt,Len(#txt)-#i)
Set #i = CHARINDEX(',',#txt)
end
insert into #tab (id) values (#txt)
RETURN
END
GO

Insert update delete trigger

Dear all I´m having trouble with my trigger.
Am I doing this at all right, right now it only works for Insert. I think I´m pretty close tho please help me if you have the time. I´m trying to store all the inserts, updates and deletes into the table customers_changelog via trigger. There is something wrong with the code I cant delete or update customers I can only insert new ones. Please help my I have been spending plenty of hours on this and just cant get this to work! :)
create table customers (
customerid int identity primary key,
name varchar(100) not null,
address varchar(100)
)
go
create table customers_changelog (
customerid int,
name varchar(100) not null,
address varchar(100),
change_user varchar(32),
change_time datetime,
change_action char(1) default 'I',
check (change_action = 'I' or change_action = 'D')
)
go
CREATE TRIGGER log_changes
ON customers
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
--
-- Check if this is an INSERT, UPDATE or DELETE Action.
--
DECLARE #customerid1 as int;
DEClARE #name1 as varchar(32);
DECLARE #address1 as varchar(100);
DECLARE #change_action1 as char(1);
DECLARE #change_time1 as datetime;
DECLARE #change_user1 as varchar(32);
select #customerid1 = c.customerid, #name1 = c.name, #address1 = c.address
from customers c, inserted i
where c.customerid = i.customerid
SET #change_time1 = CURRENT_TIMESTAMP;
SET #change_user1 = CURRENT_USER;
INSERT INTO customers_changelog(customerid,name,address,change_action,change_time,change_user)
VALUES(#customerid1,#name1,#address1,'I',#change_time1,#change_user1)
IF EXISTS(SELECT * FROM DELETED)
BEGIN
IF EXISTS(SELECT * FROM INSERTED)
INSERT INTO customers_changelog VALUES(#customerid1,#name1,#address1,'U',#change_time1,#change_user1)
ELSE
INSERT INTO customers_changelog VALUES(#customerid1,#name1,#address1,'D',#change_time1,#change_user1)
END
ELSE
IF NOT EXISTS(SELECT * FROM INSERTED) RETURN;
END
Assuming MS-SQL from syntax - So couple issues here:
1. Need to specify column lists in the "update" and "delete" inserts because the column order in the table doesn't match your inserts.
2. Can't use "inserted" data for delete insert
ALTER TRIGGER [dbo].[log_changes] ON [dbo].[customers] AFTER INSERT, UPDATE, DELETE AS
BEGIN
SET NOCOUNT ON;
DECLARE #customerid1 as int;
DEClARE #name1 as varchar(32);
DECLARE #address1 as varchar(100);
DECLARE #change_action1 as char(1);
DECLARE #change_time1 as datetime;
DECLARE #change_user1 as varchar(32);
select #customerid1 = c.customerid, #name1 = c.name, #address1 = c.address
from customers c, inserted i
where c.customerid = i.customerid
SET #change_time1 = CURRENT_TIMESTAMP;
SET #change_user1 = CURRENT_USER;
IF EXISTS(SELECT * FROM DELETED)
BEGIN
IF EXISTS(SELECT * FROM INSERTED)
INSERT INTO customers_changelog(customerid,name,address,change_action,change_time,change_user)
VALUES(#customerid1,#name1,#address1,'U',#change_time1,#change_user1)
ELSE
BEGIN
select #customerid1 = d.customerid, #name1 = d.name, #address1 = d.address
from deleted d
INSERT INTO customers_changelog(customerid,name,address,change_action,change_time,change_user)
VALUES(#customerid1,#name1,#address1,'D',#change_time1,#change_user1)
END
END
ELSE
BEGIN
IF NOT EXISTS(SELECT * FROM INSERTED) RETURN;
INSERT INTO customers_changelog(customerid,name,address,change_action,change_time,change_user)
VALUES(#customerid1,#name1,#address1,'I',#change_time1,#change_user1)
END
END

Resources