SQL Server stored procedure needs some work - sql-server

I have two tables Product and Purchase:
Product table:
ProductID = 1
PName = tv
StockQty = 10
Purchase table:
PurchaseID = 1
PID = 1
PurchaseQty = 5
Product ID is a foreign key in Purchase table. I am trying to write a stored procedure so that whenever Purchase Quantity is added in Purchase table, the main table Product should also be changed.
For example: I add purchase quantity 5. When I refresh the Product table, it should display 15. With my current stored procedure, it is updating all products and increases StockQty by 5 - instead it should do this to only the product id I chose. Please advise.
Below is my stored procedure:
CREATE PROCEDURE insertpurchase
#PID int,
#PurchaseQty int,
#StockQty int
AS
INSERT INTO [dbo].[Purchase] (PID, PurchaseQty)
VALUES (#PID, #PurchaseQty)
BEGIN
UPDATE [dbo].[Product]
SET Product.StockQty = Product.StockQty + Purchase.PurchaseQty
FROM Purchase
END
Can somebody guide what is wrong with my query?

You need to add where clause while updating.
'CREATE PROCEDURE
insertpurchase
#PID int ,
#PurchaseQty int,
#StockQty int
AS
INSERT INTO [dbo].[Purchase] (PID ,PurchaseQty ) VALUES (#PID ,#PurchaseQty )
BEGIN
UPDATE [dbo].[Product]
SET Product.StockQty = Product.StockQty + #PurchaseQty
WHERE ProductID = #PID
END '

It’s true that the stored procedure needs the filter for the ProductID on the update statement. however, to make sure both update and insert are executed properly, I made some adjustment to the code with the following
1.) I removed #StockQty because in that scenario it seems that the stored procedure only require the id of puchases item and purchase quantity
2.) I add the Transaction to make sure that both insert and update are executed properly, without it, it is possible to insert but does not update.
CREATE PROCEDURE insertpurchase
#PID int,
#PurchaseQty int
AS
BEGIN TRANSACTION
INSERT INTO [dbo].[Purchase] (PID, PurchaseQty)
VALUES (#PID, #PurchaseQty)
BEGIN
UPDATE [dbo].[Product]
SET Product.StockQty = Product.StockQty + #PurchaseQty
FROM Purchase
WHERE ProductID = #PID
COMMIT TRANSACTION
END

Related

'[07000] The executeUpdate method must not return a result set. ' in Datagrip

When I modify the data directly in the table with an update trigger, there will be an error "[07000] The executeUpdate method must not return a result set.", but there is no such error when running UPDATE Lab5.SaleOrderDetail SET Quantity = 3 WHERE OrderID = 7 AND ProductID = 74;
in the query console, and the trigger works well.
The [07000]error
The DDL of that table is
CREATE TABLE Lab5.SaleOrderDetail
(
OrderID int NOT NULL
CONSTRAINT SaleOrderDetail_SaleOrder_OrderID_fk
REFERENCES Lab5.SaleOrder,
ProductID int NOT NULL,
Quantity int,
UnitPrice int,
PRIMARY KEY NONCLUSTERED (OrderID, ProductID)
)
GO
The trigger is (I try to set coconut on, but the problem is still there)
CREATE TRIGGER ChangeLastModified
ON lab5.SaleOrderDetail
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE Lab5.SaleOrder
SET LastModified = CURRENT_TIMESTAMP
FROM lab5.SaleOrder
INNER JOIN inserted i ON SaleOrder.OrderID = i.OrderID
END
The tables in the schema
By the way, why is the trigger not listed in the list on the left

Insert and update multiple records via same stored procedure

I created this stored procedure to go through all the records in the table comparing the id (primary key) if exists and the records changed, make the necessary changes & update the record.
If the id is not in the table then insert the record. This stored procedure
compiles fine, but doesn't seem to work properly. Does this need a while loop?
ALTER PROCEDURE [dbo].[SMLineUpdate]
(
#id [int],
#Payroll_Id [int],
#ProductCode nvarchar(255),
#Description nvarchar (255),
#Qty nvarchar(255)
)
AS
IF EXISTS (SELECT Id from Smline where #id = Id) BEGIN
update dbo.SmLine
Set [Payroll_Id] = #Payroll_Id
, ProductCode = #ProductCode
, Description = #Description
, Qty = #Qty
END ELSE BEGIN
INSERT INTO SmLine ([Payroll_Id], [ProductCode], [Description], [Qty])
VALUES (#Payroll_Id, #ProductCode, #Description, #Qty)
END
Your update query is missing a where condition
update dbo.SmLine
Set [Payroll_Id] = #Payroll_Id
,ProductCode = #ProductCode
,Description = #Description
,Qty = #Qty
WHERE Id = #Id -- the query missed this where condition
IF EXISTS(SELECT Id from Smline where Id =#id)
BEGIN
update dbo.SmLine
Set [Payroll_Id]= #Payroll_Id
,ProductCode= #ProductCode
,Description = #Description
,Qty = #Qty
WHERE Id = #Id
END
ELSE
BEGIN
INSERT INTO SmLine ([Payroll_Id],[ProductCode],[Description],[Qty])
VALUES (#Payroll_Id,#ProductCode ,#Description,#Qty)
END
Your SP does not meet the requirement of insert multiple records. It works only for a single record update or inserts, you have to pass multiple id's and values respectively for update multiple so use a different approach like XML as an input parameter so u can simply do this operation for multiple by extracting the XML data.
Your update statement lacks a where statement. That is a major 'no-no', as it will (god forbid...) update all lines in the table.
Your insert statement lacks an identity insert, so consider the case where you are trying to update/insert id=5, but by now this line is deleted (not found in the where), and ids are much bigger. you would search for it -- > not find, and insert a new line (say id=101), then look for id=5 again, not find it again, and insert it again (say id=102), and so on... I don't think that's what you intended. Consider a Merge statement (when matched/when not matched) and get the best of both worlds. Also consider not deleting from the table, and instead add an 'IsDeleted' column (which allows 'reviving' a deleted row).

i want to limit the data inserting into database table on give date and time only 12

create procedure SP_insert_test #name varchar(20), #emailid varchar(20), #trainer_name varchar(50), #training_date varchar(50), #training_time varchar(50), #gymname varchar(50) , #success int out as
begin
if(
select
count(id)
from
Add_Booking_Fitness_Training
where
training_time = #training_time) > 11 print N'Number of Booking Is Complete for this time and date plz book other time';
else
insert into
Add_Booking_Fitness_Training(memeber_name, member_emailid, trainer_name, training_date, training_time, gymname)
values
(
#name,
#emailid,
#trainer_name,
#training_date,
#training_time,
#gymname
)
SELECT
SCOPE_IDENTITY()
set
#success = 1;
end
begin
set
#success = 0;
end
i have an table in which i want to insert data on give time only 12 member can insert at that time after that they get message list is full plz change the time for inserting i have create procedure its working when its reach number of 12 than its show me message but when i change the time its also show me the same message and not insert any data into database
like 26/04/2018,'6:00' i want to insert this value only 12 time after 12 this show me a message about the limit of number is reach plz change (time)
Create table Add_Booking_Fitness_Training ( id int identity primary key,
memeber_name varchar(20),
member_emailid varchar(20),
trainer_name varchar(50),
training_date varchar(50),
training_time varchar(50),
gymname varchar(50))
i just want to inserting a value into this table only 12 time for a give time like (6:00) if the number of inserting value reach to 12 than its show me the message number of values insert is reach to 12 please change the time.
i want input the value into table only 12 time for a give time 6:00Am when the value is insert into table 12 time than message come up for change time than insert value for change time
Honestly, I am completely guessing here, I still don't really know what you're asking.
I think the OP's statement of "i want input the value into table only 12 time for a give time 6:00Am when the value is insert into table 12 time than message come up for change time than insert value for change time." means that they only want a time to appear in the table up to 12 times. If it appears more than that, the INSERT fails.
This can be achieved with a check constraint and a scalar function. So, as a very simple example:
USE Sandbox;
GO
--Create a very simple table
CREATE TABLE SampleTable (TrainingTime datetime2(0));
GO
--Create the scalar function
CREATE FUNCTION TrainingAtTime (#TrainingTime datetime2(0))
RETURNS INT
AS BEGIN
DECLARE #Trainees int;
SELECT #Trainees = COUNT(*)
FROM SampleTable
WHERE TrainingTime = #TrainingTime;
RETURN #Trainees;
END
GO
--Add the check constraint
ALTER TABLE SampleTable ADD CONSTRAINT MaxTrainees CHECK (dbo.TrainingAtTime(TrainingTime) <= 12) ;
GO
--Insert first trainee
INSERT INTO SampleTable
VALUES ('2018-04-26T06:00:00');
--It works
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--insert 11 more
INSERT INTO SampleTable
VALUES ('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00');
--It works
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--Try to insert another
INSERT INTO SampleTable
VALUES ('2018-04-26T06:00:00');
--It fails
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--Use a different time
INSERT INTO SampleTable
VALUES ('2018-04-26T08:00:00');
--it works
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--Clean up
DROP TABLE SampleTable;
DROP FUNCTION TrainingAtTime;
GO
If this isn't what you're after, unfortunately I don't understand your requirements due the the language barrier (and absence of a question).

TSQL variable object reference not set to instance of an object

I'm trying to create a trigger that initialises a record over multiple tables when a master record is inserted to a master table. The code below gives the error "Object reference not set to an instance of an object."
Appraisal is the table the trigger exists for and every table concerned has an AppraisalID foreign key.
BEGIN TRANSACTION
DECLARE #appraisalid int
SET #appraisalid = (SELECT AppraisalID FROM inserted)
INSERT INTO dbo.AppraisalCPD (AppraisalID)
VALUES (#appraisalid)
COMMIT;
The code below works, but I would rather use a variable to assign the value as I need to add rows to a fair few tables.
BEGIN
INSERT INTO dbo.AppraisalCPD
(AppraisalID)
SELECT AppraisalID
FROM inserted;
END;
If anyone can suggest a way to set an appraisalid variable using the inserted row in the Appraisal table to insert rows into every other table I need to, that'd be very helpful.
I am assuming you are talking about home appraisal.
The [appraisal] table is the parent and the [owners]/[location] are the children.
Here is a play schema in tempdb.
-- just playing
use tempdb;
go
-- drop table
if object_id('appraisal') > 0
drop table appraisal
go
-- create table - home appraisal
create table appraisal
(
app_id int identity (1,1),
app_request datetime,
app_employee_id int
);
-- drop table
if object_id('owners') > 0
drop table owners
go
-- create table - the owners
create table owners
(
own_id int identity (1,1) primary key,
own_first_nm varchar(64),
own_last_nm varchar(64),
app_id int
);
-- drop table
if object_id('location') > 0
drop table location
go
-- the location
create table location
(
loc_id int identity (1,1) primary key,
loc_street varchar(64),
loc_city varchar(64),
loc_state varchar(2),
loc_zip varchar(9),
app_id int
);
go
When creating empty child records via a trigger, you either have to define default values or supply them in the trigger.
Also, I leave setting up foreign keys for you to handle.
The code for the trigger could look like the following.
-- The trigger
CREATE TRIGGER DBO.make_empty_children on dbo.appraisal
for insert, update, delete
as
BEGIN
-- nothing to do?
IF (##rowcount = 0) RETURN;
-- do not count rows
SET NOCOUNT ON;
-- delete
IF NOT EXISTS (SELECT * FROM inserted)
BEGIN
RETURN;
END
-- insert
ELSE IF NOT EXISTS (SELECT * FROM deleted)
BEGIN
-- dummy record for owners
insert into owners
(
own_first_nm,
own_last_nm,
app_id
)
select
'enter first name',
'enter last name',
app_id
from
inserted;
-- dummy record for location
insert into location
(
loc_street,
loc_city,
loc_state,
loc_zip,
app_id
)
select
'enter street',
'enter city',
'ri',
'00000',
app_id
from
inserted;
RETURN;
END
-- update
ELSE
BEGIN
RETURN;
END
END
GO
I left place holders for DELETE and UPDATE operations.
1 - Do you want to reject updates to the id (key) in the appraisal table. Probably not. This can also be taken care (prevented) of by a Foreign Key (FK).
2 - Do you want to cascade deletes? This also can be handled by a FK or in code in the trigger.
Lets see no records in the tables.
select * from appraisal;
select * from owners;
select * from location;
Like Marc / Aaron said, the inserted / deleted tables are record sets. Not a single row.
Order is not guaranteed. Use an order by if you want the records inserted by app id order.
-- Insert 2 records
insert into appraisal
(
app_request,
app_employee_id
)
values
(getdate(), 7),
(dateadd(d, 1, getdate()), 7);
-- Lets see the data
select * from appraisal;
select * from owners;
select * from location;

Why is the natural ID generation in this SQL Stored Proc creating duplicates?

I am incrementing the alphanumeric value by 1 for the productid using stored procedure. My procedure incrementing the values up to 10 records, once its reaching to 10th say for PRD0010...no more its incrementing... however, the problem is it is repeating
the same values PRD0010.. for each SP call.
What could be the cause of this?
create table tblProduct
(
id varchar(15)
)
insert into tblProduct(id)values('PRD00')
create procedure spInsertInProduct
AS
Begin
DECLARE #PId VARCHAR(15)
DECLARE #NId INT
DECLARE #COUNTER INT
SET #PId = 'PRD00'
SET #COUNTER = 0
SELECT #NId = cast(substring(MAX(id), 4, len(MAX(id))) as int)
FROM tblProduct group by left(id, 3) order by left(id, 3)
--here increse the vlaue to numeric id by 1
SET #NId = #NId + 1
--GENERATE ACTUAL APHANUMERIC ID HERE
SET #PId = #PId + cast(#NId AS VARCHAR)
INSERT INTO tblProduct(id)values (#PId)
END
Change
SELECT #NId = cast(substring(MAX(id), 4, len(MAX(id))) as int)
FROM tblProduct group by left(id, 3) order by left(id, 3)
To
SELECT TOP 1
#NId = cast(substring(id, 4, len(id)) as int)
FROM tblProduct order by LEN(id) DESC, ID DESC
You have to remember that
PRD009
is always greater than
PRD0010
or
PRD001
All in all, I think your approach is incorrect.
Your values will be
PRD00
PRD001
...
PRD009
PRD0010
PRD0011
...
PRD0099
PRD00100
This will make sorting a complete nightmare.
In addition to astander's analysis, you also have a concurrency issue.
The simple fix would be to add this at the beginning of your proc:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
And add a COMMIT at the end. Otherwise, two callers of this stored proc will get the same MAX/TOP 1 value from your table, and insert the same value.
Also, you can and should prevent these duplicates from existing by adding a key to your table, for this column. If you already have a PRIMARY KEY on this table, you can add an additional key using a UNIQUE constraint. This will prevent duplicates occurring in the future, no matter what programming errors occur. E.g.
ALTER TABLE tblProduct ADD CONSTRAINT UQ_Product_ID UNIQUE (ID)

Resources