How to deal with Stored Procedure? - sql

Hello I am new in creating stored procedure can you help me how to do this.
Error:
Incorrect syntax near the keyword 'AS'.
Must declare scalar variable #Serial.
CREATE PROCEDURE sp_SIU
-- Add the parameters for the stored procedure here
#Serial varchar(50),
#Part varchar(50),
#Status varchar(50),
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
/*SET NOCOUNT ON;*/
-- Insert statements for procedure here
--where in my form if i enter serial number it will show select values
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
--Then if is correct it will Update Status on combobox
Update Table1 SET
Status=#Status
where SerialNumber=#SerialNumber
--then Insert Serial Number,Parnumber to Table 2
DECLARE #Count int
select #Count = Count(SerialNumber) from Table1 WHERE SerialNumber = #Serial
IF #Count = 0
BEGIN
INSERT INTO Table2 (SerialNumber,PArtNumber)
VALUES
(#Serial, #Part)
END
RETURN #Count
RETURN
Edit: Moved Updated info posted as an answer into Question
Oops my post is not that kind a miss.
It is possible to join this 3 sql string in one stored procedure?
Scenario:
{
What i have to do in my form is that i will enter serial number to txtserial.text by using the select sql it will show serialnumber,partnumber and status on lblserial.text,lblpartnumber.text and lblstatus.text.
And i will compare:
txtserial.text == lblserial.text
txtpartnumber.text == lblpartnumber.text
for my error handler.
{
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
}
Then if they are equal then:
I will update my Status from cbostatus.text if serial and part is correct then use sql upate.
{
Update Table1 SET
Status=#Status,
Modifiedby=#username,
DateModified=#Date
where SerialNumber=#Serial
}
Then insert serialnumber, using sql insert to another table.
{
INSERT INTO Table2 (SerialNumber,DateCreated,Createdby)
VALUES
(#Serial,#date,#username)
}
something likethis.
")

You have a rogue comma here
#Status varchar(50),
AS
and the name lurches between #Serial and #SerialNumber are these intended to be 2 different parameters?
Also what is the purpose of this line?
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
Currently it will just send back a 3 column result set to the calling application. Is that what it is intended to do (it doesn't seem to match the following comment which seems to imply it is meant to be some kind of check)?

Yes, you can execute 3 SQL statements inside one stored procedure. You probably want to declare some local variables inside your sproc to hold the intermediate results, i.e.
CREATE PROCEDURE BLAHBLAH
#SerialNumber VarChar(50)
AS
BEGIN
DECLARE #partnumber varchar(50);
SELECT #partnumber = partnumber FROM Table WHERE serialnumber = #SerialNumber;
...
SELECT #partnumber; --- return as recordset
RETURN #partnumber; --- return as return value
END
Then you can later insert #partnumber, test #partnumber, return #partnumber etc. I don't quite understand what you want to do; seems like you mostly want to look up a partnumber based on a serial number, but you want to do some uniqueness tests also. It would help if you could clarify the goal a bit more.
I recommend you ignore the user interface stuff for the moment. Write yourself some nice clean stored procedures that encapsulate the transaction and will do the right thing even if fired off at the same time from two different connections. Get everything working to your satisfaction in your SQL environment. Then go back to the user interface.

Oops my post is not that kind a miss.
It is possible to join this 3 sql string in one stored procedure?
Scenario:
What I have to do in my form is that I will enter serial number to txtserial.text by using the select sql it will show serialnumber,partnumber and status on lblserial.text,lblpartnumber.text and lblstatus.text.
AndI will compare:
txtserial.text == lblserial.text
txtpartnumber.text == lblpartnumber.text
for my error handler.
{
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
}
Then if they are equal then:
I will update my Status from cbostatus.text if serial and part is correct then use sql update.
{
Update Table1
SET Status = #Status,
Modifiedby = #username,
DateModified = #Date
where SerialNumber = #Serial
}
Then insert serialnumber, using sql insert to another table.
{
INSERT INTO Table2(SerialNumber, DateCreated, Createdby)
VALUES(#Serial, #date, #username)
}
something like this.

Related

Output Always 1

I have a stored procedure and am using a Merge Statement to Insert and Update. This aspect is working as I require.
However, the output when inserting the record is always 1 and I cannot see why? I would be grateful if someone could review this procedure and let me know what I could be doing wrong,.
CREATE PROCEDURE [dbo].[FileAdd]
#FileId int,
#FileData varbinary(max),
#ContentType Varchar(100),
#OperatorId int
AS
BEGIN
--In Memory Table to
DECLARE #MergeOutput TABLE
(
Id INT
);
--Merge needs a table to Merge with so using a CTE
WITH CTE AS (
SELECT #FileId as FileId)
--Merge
MERGE INTO [dbo].[Files] as T
USING CTE AS S
ON T.FileId = S.FileId
WHEN NOT MATCHED THEN
INSERT (
FileData,
ContentType,
OperatorIdCreated,
OperatorIdUpdated
)
VALUES(
#FileData,
#ContentType,
#OperatorId,
#OperatorId
)
WHEN MATCHED THEN
UPDATE SET
FileData = #FileData,
ContentType= #ContentType,
OperatorIdUpdated = #OperatorId,
Updated = GetDate()
OUTPUT
INSERTED.FileId
INTO #MergeOutput;
SELECT * FROM #MergeOutput;
END
GO
The reason you are getting 1 is because that is what is being UPDATED or INSERTED. When it's the UPDATED value, then it is the value are passing into #FileID.
With the OUTPUT clause:
INSERTED Is a column prefix that specifies the value added by the
insert or update operation.
Thus, what ever value is UPDATED (which is #FileID) or INSERTED (which will be whatever your FileID table logic is) this will be returned in your code. If you are always getting 1, then you must me always updating the column for FileID = 1.
Changing your bottom to inserted.* would show you this, as it would OUTPUT the updated row.
Check the demo here.

Stored procedure with AS MERGE not returning anything?

EDIT: Sequential invoice numbering is the law in multiple countries.
EDIT: Poor variable naming on my part suggested I wanted to use my generated Id as a key. This is not the case. Should have stuck with 'invoiceNumber'.
I have the exact same question as posed here https://stackoverflow.com/a/24196374/1980516
However, since the proposed solution threw a syntax error, I've adapted it to use a cursor.
First, there is the stored procedure that generates a new Nr, for a given Business+Year combination:
CREATE PROCEDURE PROC_NextInvoiceNumber #businessId INT, #year INT, #Nr NVARCHAR(MAX) OUTPUT
AS MERGE INTO InvoiceNextNumbers ini
USING (VALUES (#businessId, #year)) Incoming(BusinessId, Year)
ON Incoming.BusinessId = ini.BusinessId AND Incoming.Year = ini.Year
WHEN MATCHED THEN UPDATE SET ini.Nr = ini.Nr + 1
WHEN NOT MATCHED BY TARGET THEN INSERT (BusinessId, Year, Nr)
VALUES(#businessId, #year, 1)
OUTPUT INSERTED.Nr;
Then, using that stored procedure, I've created an INSTEAD OF INSERT trigger:
CREATE TRIGGER TRIG_GenerateInvoiceNumber ON Invoices INSTEAD OF INSERT
AS
BEGIN
DECLARE #BusinessId INT
DECLARE #InvoiceId INT
DECLARE #BillingDate DATETIME2(7)
-- Cursors are expensive, but I don't see any other way to call the stored procedure per row
-- Mitigating factor: Mostly, we're only inserting one Invoice at a time
DECLARE InsertCursor CURSOR FAST_FORWARD FOR
SELECT BusinessId, Id, BillingDate FROM INSERTED
OPEN InsertCursor
FETCH NEXT FROM InsertCursor
INTO #BusinessId, #InvoiceId, #BillingDate
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #year INT
SET #year = year(#BillingDate)
DECLARE #Number NVARCHAR(MAX)
EXEC PROC_NextInvoiceNumber #BusinessId, #year, #Number OUTPUT
-- SET #Number = 'this works'
INSERT INTO Invoices (BusinessId, Id, BillingDate, Number)
VALUES (#BusinessId, #InvoiceId, #BillingDate, #Number)
FETCH NEXT FROM InsertCursor
INTO #BusinessId, #InvoiceId, #BillingDate
END
CLOSE InsertCursor
DEALLOCATE InsertCursor
END
If I uncomment SET #Number = 'this works', then in my database that exact string ('this works') is successfully set in Invoice.Number.
Somehow, my OUTPUT parameter is not set and I can't figure out why not.. Can someone shed a light on this?
EDIT update in response to comments (thank you):
I have a composite key (BusinessId, Id) for Invoice. The desired end result is a unique Invoice Identifier Number of the form '20180001' that is a continuous sequence of numbers within the businessId. So business 1 has invoice Numbers 20180001, 20180002, 20180003 and business 2 also has invoice numbers 20180001, 20180002, 20180003. (But different composite primary keys)
I don't want that cursor either, but I saw no other way within the framework as suggested by the question I refer to up above.
Manual call of PROC_NextInvoiceNumber with existing business id and year returns NULL.
If I try to set Id in PROC_NextInvoiceNumber, I get A MERGE statement must be terminated by a semi-colon (;). if I set it inside the MERGE or The multi-part identifier "INSERTED.Nr" could not be bound. if I set outside the MERGE.
Your OUTPUT parameter is never set. You are using the OUTPUT clause of the MERGE statement to create a result set. This is unrelated to assigning a value to a parameter.
MERGE INTO..
USING ... ON ...
WHEN MATCHED THEN UPDATE ...
WHEN NOT MATCHED BY TARGET THEN INSERT ...
OUTPUT INSERTED.Nr; /* <-- HERE this is the OUTPUT *clause* */
Change the code to actually assign something to #Nr:
SET #Nr = ...
The typical way is to use the OUTPUT clause to store the desired value into a table variable and then assign the value to the desired output *variable:
DECLARE #t TABLE (Nr NVARCHAR(MAX));
MERGE INTO..
USING ... ON ...
WHEN MATCHED THEN UPDATE ...
WHEN NOT MATCHED BY TARGET THEN INSERT ...
OUTPUT INSERTED.Nr INTO #t;
SELECT #Nr = Nr FROM #t;

SQL stored procedure inserting duplicate OrderNumber

I searched the Internet for days, no effort, maybe I cant ask in a right way.
I have a sql table like this:
create table Items
(
Id int identity(1,1),
OrderNumber varchar(7),
ItemName varchar(255),
Count int
)
Then I have a stored procedure inserting items, on demand creating new OrderNumber:
create procedure spx_insertItems
#insertNewOrderNr bit,
#orderNumber varchar(7),
#itemName varchar(255),
#count int
as
begin
set nocount on;
if (#insertNewOrderNr = 1)
begin
declare #newNr = (select dbo.fun_getNewOrderNr())
INSERT INTO Items (OrderNumber, ItemName, Count) values (#newNr, #itemName, #count)
select #newNr
end
else
begin
INSERT INTO Items (OrderNumber, ItemName, Count) values (#orderNumber, #itemName, #count)
select scope_identity()
end
end
Finally there is a user defined function returning new OrderNumber:
create function dbo.fun_getNewOrderNr
()
return varchar(7)
as
begin
/* this func works well */
declare #output varchar(7)
declare #currentMaxNr varchar(7)
set #currentMaxNr = (isnull((select max(OrderNumber) from Items), 'some_default_value_here')
/* lets assume the #currentMaxNr is '01/2014', here comes logic that increments to #newValue='02/2014' and sets to #output, so: */
set #output = #newValue
return #output
end
Into Items can be inserted items that do as well that do not belong to any OrderNumber.
Whether an Item should become new OrderNumber, the procedure is called with #insertNewOrderNr=1, returns the new order number, that can be used to insert next items with that OrderNumber while #insertNewOrderNr=0.
Occasionally there happens that there come simultaneously 2 requests to #insertNewOrderNr and THERE IS THE PROBLEM - Items, that should correspond with different OrderNumbers get the same OrderNumber.
I tried to use transaction with no success.
The table structure cant be modified by me.
What would be the right way to ensure, that there won't be used the same newOrderNumber when simultaneous requests to the procedure come?
I am stuck here for a long time till now. Please, help.
You will have that problem as long as you use MAX(OrderNumber).
You might consider using sequences:
Create sequence
CREATE SEQUENCE dbo.OrderNumbers
AS int
START WITH 1
INCREMENT BY 1
NO CACHE;
GO
CREATE SEQUENCE dbo.OrderNumberYear
AS int
START WITH 2014
INCREMENT BY 1
NO CACHE;
SELECT NEXT VALUE FOR dbo.OrderNumberYear; --Important, run this ONE time after creation, this years value must be returned one initial time to work correctly.
Insert code
DECLARE #orderNumberYear INT = (SELECT CONVERT(INT, current_value) FROM sys.sequences WHERE name = 'OrderNumberYear');
IF(#orderNumberYear < YEAR(GETDATE()))
BEGIN
SELECT #orderNumberYear = NEXT VALUE FOR dbo.OrderNumberYear;
ALTER SEQUENCE dbo.OrderNumbers RESTART WITH 1 ;
END
IF(#orderNumberYear != YEAR(GETDATE()))
RAISERROR(N'Order year sequence is out of sync.', 16, 1);
DECLARE #newNr VARCHAR(15) = CONCAT(FORMAT(NEXT VALUE FOR dbo.OrderNumbers, '00/', 'en-US'), #orderNumberYear);
INSERT INTO Items (OrderNumber, ItemName, Count) values (#newNr, #itemName, #count)
SELECT #newNr
The duplicity still occured, not so often, but did.
The trick I finally used to get around didn't find itself inside SQL. Since the DB is always used by ONLY one web app that is used by several users, this is the solution:
in all (about 5) places in my VB.NET code I surrounded the myCommand.ExecuteScalar() with SyncLock (read more) statement that DID the trick :)

How to selectively return rows inside a stored procedure on SQL Server?

I have a base stored procedure simply returning a select from the database, like this:
CREATE PROCEDURE MyProcedure
AS
BEGIN
SELECT * FROM MyTable
END
GO
But now I need to execute some logic for every row of my select. According to the result I need to return or not this row. I would have my select statement running with a cursor, checking the rule and return or not the row. Something like this:
CREATE PROCEDURE MyProcedure
AS
BEGIN
DECLARE CURSOR_MYCURSOR FOR SELECT Id, Name FROM MyTable
OPEN CURSOR_MYCURSOR
FETCH NEXT FROM CURSOR_MYCURSOR INTO #OUTPUT1, #OUTPUT2
WHILE (##FETCH_STATUS=0)
BEGIN
IF (SOME_CHECK)
SELECT #OUTPUT1, #OUTPUT2
ELSE
--WILL RETURN SOMETHING ELSE
END
END
GO
The first problem is that everytime I do SELECT #OUTPUT1, #OUTPUT2 the rows are sent back as different result sets and not in a single table as I would need.
Sure, applying some logic to a row sounds like a "FUNCTION" job. But I can't use the result of the function to filter the results being selected. That is because when my check returns false I need to select something else to replace the faulty row. So, I need to return the faulty rows so I can be aware of them and replace by some other row.
The other problem with this method is that I would need to declare quite a few variables so that I can output them through the cursor iteration. And those variables would need to follow the data types for the original table attributes and somehow not getting out of sync if something changes on the original tables.
So, what is the best approach to return a single result set based on a criteria?
Thanks in advance.
I recommend use of cursors but easy solution to your question would be to use table variable or temp table
DECLARE #MyTable TABLE
(
ColumnOne VARCHAR(20)
,ColumnTwo VARCHAR(20)
)
CREATE TABLE #MyTable
(
ColumnOne VARCHAR(20)
,ColumnTwo VARCHAR(20)
)
than inside your cursors you can insert records that match your logic
INSERT INTO #MyTable VALUES (#Output1, #Output2)
INSERT INTO #MyTable VALUES (#Output1, #Output2)
after you done with cursor just select everything from table
SELECT * FROM #MyTable
SELECT * FROM #MyTable

how to write a if else statement to insert query for showing the error in stored procedure

how to write a if else statement to insert query for showing the error in stored procedure
below is my stored procedure.I want to show a error message when already inserted student_id is insert again..Student_id is primary key so its show error in my code but i dono how to get that error and show ....how to do friends plz help me.....
ALTER PROCEDURE [dbo].[spinsertstudentapplication]
-- Add the parameters for the stored procedure here
#Student_id nvarchar(50),
#Select_Country nvarchar(50),
#Select_State nvarchar(50),
#Select_Franchise nvarchar(50),
#Select_Sensei nvarchar(50),
#Enter_Student_Name nvarchar(50),
#Enter_Student_Address nvarchar(50),
#Students_Father_Name nvarchar(50),
#Student_DOB datetime,
#Gender bit,
#Group nvarchar(50),
#Enter_Kyu nvarchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
insert into StudentApplication(Student_id,Select_Country,Select_State,Select_Franchise,Select_Sensei,Enter_Student_Name,Enter_Student_Address,Students_Father_Name,Student_DOB,Gender,[Group],Enter_Kyu)values(#Student_id,#Select_Country,#Select_State,#Select_Franchise,#Select_Sensei,#Enter_Student_Name,#Enter_Student_Address,#Students_Father_Name,#Student_DOB,#Gender,#Group,#Enter_Kyu)
END
You can add an output parameter(int/bit) in Sp and set value to that parameter whether record exist or not. Then you can check the output parameter value in Front End. Below I've added an output parameter #RecordExist as bit and setting the value 1 when record already exists otherwise setting 0. In front end you can get the Parameter value from SqlCommand after executing the Sp. (SqlCommand.Parameters["#RecordExist"].Value)
ALTER PROCEDURE [dbo].[spinsertstudentapplication]
-- Add the parameters for the stored procedure here
#Student_id nvarchar(50),
#Select_Country nvarchar(50),
#Select_State nvarchar(50),
#Select_Franchise nvarchar(50),
#Select_Sensei nvarchar(50),
#Enter_Student_Name nvarchar(50),
#Enter_Student_Address nvarchar(50),
#Students_Father_Name nvarchar(50),
#Student_DOB datetime,
#Gender bit,
#Group nvarchar(50),
#Enter_Kyu nvarchar(50),
#RecordExist bit output -- newly added parameter
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 Exists (Select * from StudentApplication where Student_id = #Student_id)
Begin
Select #RecordExist = 1
return
End
Else
Begin
insert into StudentApplication (Student_id, Select_Country, Select_State, Select_Franchise, Select_Sensei, Enter_Student_Name, Enter_Student_Address, Students_Father_Name, Student_DOB, Gender, [Group], Enter_Kyu)
Select #Student_id, #Select_Country, #Select_State, #Select_Franchise, #Select_Sensei, #Enter_Student_Name, #Enter_Student_Address, #Students_Father_Name, #Student_DOB, #Gender, #Group, #Enter_Kyu
Select #RecordExist = 0
return
End
END
As Dean 'codeka' Harding mentioned in the comment, it would be helpful to know how you're calling the stored proc. But here are some general suggestions.
First, a common convention for stored procedures is to return 0 on success and non-zero value on error (you can use an output parameter for error codes, but it's kind of redundant).
Second, before attempting to insert a value, you should check if it already exists. E.g. here is some pseudo code:
if exists (select 1 from StudentApplication where Student_ID = #Student_ID)
begin
raiserror('Student ID already exists.', 16, 1)
return 1 -- Your caller would need to know that 1 identifies existing record
end
Notice that in this example, T-SQL code exits after calling raiserror, so you need to handle this as an exception if you call the stored proc from C#/VB.NET/etc. Alternatively, you can omit the raiserror call and just have the stored proc return an expected (by the client) error code.
Then there is still a minor possibility that a duplicate would be inserted, but I think that it would generate a fatal error that you would need to handle in the client code (error handling is dependent on the client; in C#, you will probably get a SqlException which you can query for specific code).
Another option would be to put a transaction around the code that checks for existing record and then inserts a new one.
If you want to handle error in C#, you need to take care of two things. First, check the return code and process non-zero values accordingly (the C# client and stored proc have to agree on the meaning of each error code). Then, you also need to handle SqlExceptions. The State and Number properties of the SqlException object can help you identify the problem. Keep in mind that for error messages defined on the fly (as in my example), Number will alway return 50,000 (I think).
You can add an output parameter at the top of your SP:
#ErrorOutput INT OUTPUT
Then add:
IF EXISTS (SELECT 1 FROM StudentApplication WHERE Student_id=#Student_id)
SET #ErrorOutput = -1;
RETURN #ErrorOutput;
ELSE
-- Insert statement