Return Bigint in a stored procedure - sql

I have a stored procedure which has to return a bigint as output. below if the definition.
In the body, I'm inserting a row in a table and returning the identity using ##Identity in #LogID output variable.
Everything works except the return part. I have tried casting, converting, selecting and setting the #LogID to ##identity but no luck.
The stored proc is called by enterprise library's Logging block. I'm using Write method of Logwriter class. This is a standard class provided by Microsoft and we have not modified it. I can't show you the code which calls the procedure as this is a DLL and don't have the source code. Anyway, I'm pretty sure it's not the C# code as I get a SQLException so it is something in the sql. The code below is for brevity and there are lots of other columns which I have removed.They are all provided as input parameters.
I'm sure it's a stupid thing, but I'm somehow missing it.
CREATE PROCEDURE [dbo].[WriteLog]
(
#EventID int,
#Priority int,
#LogId bigint OUTPUT
)
INSERT INTO [Log] (EventID, Priority)
VALUES(#EventID,#Priority)
SET #LogID = ##identity
Go

Stored procedures can only return int. So you will need to use the output parameter.
declare #CapturedLogID bigint;
exec dbo.WriteLog #EventID = 42, #Priority = 1337, #LogID = #CapturedLogID output;
In the above example, after the procedure executes, #CapturedLogID stores the value set within the stored procedure (##identity, which, as others have pointed out, should be changed to scope_identity() immediately).
Edit: From C#, to call the procedure with an out parameter:
using (var cmd = new SqlCommand()) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.WriteLog";
cmd.Parameters.Add("EventID", SqlDbType.Int).Value = 42;
cmd.Parameters.Add("Priority", SqlDbType.Int).Value = 1337;
cmd.Parameters.Add("LogID", SqlDbType.BigInt).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
long logID = (long)cmd.Parameters["LogID"].Value;
}
Note that I've only included the code for setting up and executing the SqlCommand object.

Related

parameterizing all JOIN data in a large UPDATE operation

I have an app that makes a bunch of updates to objects hydrated with data from an SQL Server table and then writes the updates objects' data back to the DB in one query. I'm trying to convert this into a parameterized query so that I don't have to do manual escaping, conversions, etc.
Here's the most straightforward example query:
UPDATE TestTable
SET [Status] = DataToUpdate.[Status], City = DataToUpdate.City
FROM TestTable
JOIN
(
VALUES --this is the data to parameterize
(1, 0, 'A City'),
(2, 0, 'Another City')
) AS DataToUpdate(Id, [Status], City)
ON DataToUpdate.Id = TestTable.Id
I've also played around with using OPENXML to do this, but I'm still forced to write a bunch of escaping code when adding the values to the query. Any ideas on how to make this more elegant? I am open to ADO.NET/T-SQL solutions or platform-agnostic solutions.
One thought I had (but I don't really like how dynamic this is) is to dynamically create parameters and then add them to an ADO.NET SqlConnection, e.g.
for(int i = 0; i < data.Length; i++)
{
string paramPrefix = string.Format("#Item{0}", i);
valuesString.AppendFormat("{0}({1}Status)", Environment.NewLine, paramPrefix);
var statusParam = new SqlParameter(
string.Format("{0}Status", paramPrefix),
System.Data.SqlDbType.Int)
{ Value = data[i].Status };
command.Parameters.Add(statusParam);
}
I'm not exactly sure how you store your application data (and I don't have enough rep points to post comments) so I will ASSUME that the records are held in an object CityAndStatus which is comprised of int Id, string Status, string City held in a List<CityAndStatus> called data. That way you can deal with each record one at a time. I made Status a string so you can convert it to an int in your application.
With those assumptions:
I would create a stored procedure https://msdn.microsoft.com/en-us/library/ms345415.aspx in SQL Server that updates your table one record at at time.
CREATE PROCEDURE updateCityData (
#Id INT
,#Status INT
,#City VARCHAR(50)
)
AS
BEGIN TRAN
UPDATE TestTable
SET [Status] = #Status
,City = #City
WHERE Id = #Id
COMMIT
RETURN
GO
Then I would call the stored procedure https://support.microsoft.com/en-us/kb/310070 from your ADO.NET application inside a foreach loop that goes through each record that you need to update.
SqlConnection cn = new SqlConnection(connectionString);
cn.Open();
foreach (CityAndStatus item in data)
{
SqlCommand cmd = new SqlCommand("updateCityData",cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Id", item.Id);
cmd.Parameters.AddWithValue("#Status", Convert.ToInt32(item.Status));
cmd.Parameters.AddWithValue("#City", item.User);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
cn.Close();
After that you should be good. The one thing left that might stand in your way is SQL Server makes web application users grant permission to execute stored procedures. So in SQL Server you may have to do something like this to allow your application to fire the stored proc.
GRANT EXECUTE
ON updateCityData
TO whateverRoleYouHaveGivenPermissionToExecuteStoredProcedures
Good Luck

Procedure or function !!! has too many arguments specified

I am developing my very first stored procedure in SQL Server 2008 and need advice concerning the errors message.
Procedure or function xxx too many arguments specified
which I get after executing the stored procedure [dbo].[M_UPDATES] that calls another stored procedure called etl_M_Update_Promo.
When calling [dbo].[M_UPDATES] (code see below) via right-mouse-click and ‘Execute stored procedure’ the query that appears in the query-window is:
USE [Database_Test]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[M_UPDATES]
SELECT 'Return Value' = #return_value
GO
The output is
Msg 8144, Level 16, State 2, Procedure etl_M_Update_Promo, Line 0
Procedure or function etl_M_Update_Promo has too many arguments specified.
QUESTION: What does this error message exactly mean, i.e. where are too many arguments? How to identify them?
I found several threads asking about this error message, but the codes provided were all different to mine (if not in another language like C# anyway). So none of the answers solved the problem of my SQL query (i.e. SPs).
Note: below I provide the code used for the two SPs, but I changed the database names, table names and column names. So, please, don’t be concerned about naming conventions, these are only example names!
(1) Code for SP1 [dbo].[M_UPDATES]
USE [Database_Test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ M_UPDATES] AS
declare #GenID bigint
declare #Description nvarchar(50)
Set #GenID = SCOPE_IDENTITY()
Set #Description = 'M Update'
BEGIN
EXEC etl.etl_M_Update_Promo #GenID, #Description
END
GO
(2) Code for SP2 [etl_M_Update_Promo]
USE [Database_Test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [etl].[etl_M_Update_Promo]
#GenId bigint = 0
as
declare #start datetime = getdate ()
declare #Process varchar (100) = 'Update_Promo'
declare #SummeryOfTable TABLE (Change varchar (20))
declare #Description nvarchar(50)
declare #ErrorNo int
, #ErrorMsg varchar (max)
declare #Inserts int = 0
, #Updates int = 0
, #Deleted int = 0
, #OwnGenId bit = 0
begin try
if #GenId = 0 begin
INSERT INTO Logging.dbo.ETL_Gen (Starttime)
VALUES (#start)
SET #GenId = SCOPE_IDENTITY()
SET #OwnGenId = 1
end
MERGE [Database_Test].[dbo].[Promo] AS TARGET
USING OPENQUERY( M ,'select * from m.PROMO' ) AS SOURCE
ON (TARGET.[E] = SOURCE.[E])
WHEN MATCHED AND TARGET.[A] <> SOURCE.[A]
OR TARGET.[B] <> SOURCE.[B]
OR TARGET.[C] <> SOURCE.[C]
THEN
UPDATE SET TARGET.[A] = SOURCE.[A]
,TARGET.[B] = SOURCE.[B]
, TARGET.[C] = SOURCE.[c]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([E]
,[A]
,[B]
,[C]
,[D]
,[F]
,[G]
,[H]
,[I]
,[J]
,[K]
,[L]
)
VALUES (SOURCE.[E]
,SOURCE.[A]
,SOURCE.[B]
,SOURCE.[C]
,SOURCE.[D]
,SOURCE.[F]
,SOURCE.[G]
,SOURCE.[H]
,SOURCE.[I]
,SOURCE.[J]
,SOURCE.[K]
,SOURCE.[L]
)
OUTPUT $ACTION INTO #SummeryOfTable;
with cte as (
SELECT
Change,
COUNT(*) AS CountPerChange
FROM #SummeryOfTable
GROUP BY Change
)
SELECT
#Inserts =
CASE Change
WHEN 'INSERT' THEN CountPerChange ELSE #Inserts
END,
#Updates =
CASE Change
WHEN 'UPDATE' THEN CountPerChange ELSE #Updates
END,
#Deleted =
CASE Change
WHEN 'DELETE' THEN CountPerChange ELSE #Deleted
END
FROM cte
INSERT INTO Logging.dbo.ETL_log (GenID, Startdate, Enddate, Process, Message, Inserts, Updates, Deleted,Description)
VALUES (#GenId, #start, GETDATE(), #Process, 'ETL succeded', #Inserts, #Updates, #Deleted,#Description)
if #OwnGenId = 1
UPDATE Logging.dbo.ETL_Gen
SET Endtime = GETDATE()
WHERE ID = #GenId
end try
begin catch
SET #ErrorNo = ERROR_NUMBER()
SET #ErrorMsg = ERROR_MESSAGE()
INSERT INTO Logging.dbo.ETL_Log (GenId, Startdate, Enddate, Process, Message, ErrorNo, Description)
VALUES (#GenId, #start, GETDATE(), #Process, #ErrorMsg, #ErrorNo,#Description)
end catch
GO
You invoke the function with 2 parameters (#GenId and #Description):
EXEC etl.etl_M_Update_Promo #GenID, #Description
However you have declared the function to take 1 argument:
ALTER PROCEDURE [etl].[etl_M_Update_Promo]
#GenId bigint = 0
SQL Server is telling you that [etl_M_Update_Promo] only takes 1 parameter (#GenId)
You can alter the procedure to take two parameters by specifying #Description.
ALTER PROCEDURE [etl].[etl_M_Update_Promo]
#GenId bigint = 0,
#Description NVARCHAR(50)
AS
.... Rest of your code.
Use the following command before defining them:
cmd.Parameters.Clear()
This answer is based on the title and not the specific case in the original post.
I had an insert procedure that kept throwing this annoying error, and even though the error says, "procedure....has too many arguments specified," the fact is that the procedure did NOT have enough arguments.
The table had an incremental id column, and since it is incremental, I did not bother to add it as a variable/argument to the proc, but it turned out that it is needed, so I added it as #Id and viola like they say...it works.
For those who might have the same problem as me, I got this error when the DB I was using was actually master, and not the DB I should have been using.
Just put use [DBName] on the top of your script, or manually change the DB in use in the SQL Server Management Studio GUI.
Yet another cause of this error is when you are calling the stored procedure from code, and the parameter type in code does not match the type on the stored procedure.
I feel ashamed for even having to post this, but it might help someone in the future. Make sure you don't have a typo in your function call!
I kept getting this error trying to call a function and couldn't figure out why. My function and call had the same number of arguments (or so I thought).
Here's my function call:
SELECT FORMAT_NAME(A.LASTNAME, A.FIRSTNAME, A,MIDDLENAME)
It's easier to see in Stack Overflow, but it wasn't so obvious in SSMS that I had a comma in place of a period for A.MIDDLENAME.
SELECT FORMAT_NAME(A.LASTNAME, A.FIRSTNAME, A.MIDDLENAME)
Simple user error.
In addition to all the answers provided so far, another reason for causing this exception can happen when you are saving data from list to database using ADO.Net.
Many developers will mistakenly use for loop or foreach and leave the SqlCommand to execute outside the loop, to avoid that make sure that you have like this code sample for example:
public static void Save(List<myClass> listMyClass)
{
using (var Scope = new System.Transactions.TransactionScope())
{
if (listMyClass.Count > 0)
{
for (int i = 0; i < listMyClass.Count; i++)
{
SqlCommand cmd = new SqlCommand("dbo.SP_SaveChanges", myConnection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#ID", listMyClass[i].ID);
cmd.Parameters.AddWithValue("#FirstName", listMyClass[i].FirstName);
cmd.Parameters.AddWithValue("#LastName", listMyClass[i].LastName);
try
{
myConnection.Open();
cmd.ExecuteNonQuery();
}
catch (SqlException sqe)
{
throw new Exception(sqe.Message);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
myConnection.Close();
}
}
}
else
{
throw new Exception("List is empty");
}
Scope.Complete();
}
}
You either have to double check the Parameters on both side (StoredProcedure And Code):
Make Sure they are the same on both ends regarding to the number of them.
Make Sure you have NOT changed your StoredProcedure code and forgot to Execute it, nothing bad happens if you hit F5 to make sure have all the changes committed and saved.
Make Sure you you have the same naming convention on both sides (Not Likely to be the cause but it worth a shot).

EF 4.1 Code First: Execute Stored Proc and return IDENT

I'm attempting to use context.Database to execute a stored proc that inserts a record and returns the identity. I know my proc works, using either an output parameter, select, or return, but I cannot get either ExecuteSqlCommand or SqlQuery<int> to return the identity.
Using a stored proc with return,
INSERT INTO TableA (FieldA, FieldB)
RETURN SCOPE_IDENTITY
I see a return value of 1010966 from SSMS
DECLARE #return_value int
EXEC #return_value = spInsertRecord
SELECT 'Return Value' = #return_value
But nothing I have done in my application returns a valid value.
Int64 result = context.Database.ExecuteSqlCommand
always returns 3 and I get a casting error referencing Decimal when using SqlQuery<int>.
I made an attempt with an output parameter, but wasn't able to get the code to execute properly, so have focused on the app consuming either SELECT SCOPE_IDENTITY or RETURN SCOPE_IDENTITY
Short of creating models to replace the logic contained in the stored procedure, I'm open to any and all suggestions for returning the value for both the Stored Proc and application level.
Here is how you can get this to work. In this example, my EntityManager is just a class that inherits from DBContext.
Test Table:
CREATE TABLE [dbo].[TableA](
[ID] [int] IDENTITY(1,1) NOT NULL,
[FieldA] [nvarchar](50) NULL,
[FieldB] [nvarchar](50) NULL
) ON [PRIMARY]
Stored Proc:
CREATE PROC spInsertRecord
#FieldA NVARCHAR(50),
#FieldB NVARCHAR(50)
AS
INSERT TableA
(FieldA, FieldB)
VALUES
(#FieldA, #FieldB)
SELECT CAST(##IDENTITY AS INT)
Calling Code (VB.NET)
Public Shared Function AddRecord(FieldA As String, FieldB As String) As Integer
Try
Using EM As New EntityManager
Return EM.Database.SqlQuery(Of Integer)(
"EXEC spInsertRecord #FieldA, #FieldB",
New SqlParameter("FieldA", FieldA),
New SqlParameter("FieldB", FieldB)).SingleOrDefault
End Using
Catch ex As Exception
Throw New ApplicationException("An error occurred while executing AddRecord", ex)
End Try
End Function
Consuming Code:
Return AddRecord("Tom", "Halladay")
My result is the incrementing return value. Hope this helps.

SQL Server 2005 Stored Procedure Using Table Variable Doesn't Return ResultSet to VB6 App

I have a stored procedure which selects a single field from a single row (based on a conditional statement) and stores it in a local variable. Based on the value of this field I update the same field in the same row of the table (using the same conditional statement). So the procedure first does a select into a local variable and then later updates the same field of the same row. The procedure then returns a result set via a select on a table variable (I have also attempted using a temporary table). The result set does not contain the variable or field I have updated. It doesn't even include any fields from this table.
The procedure works correctly when called from either Management Studio or from a test C# application. However, when called from my VB6 app the result set is not returned. All database updates are still performed however.
I have tried writing the stored procedure with a transaction and without, with TRY...CATCH and without, and both at the same time. I've tried various combinations of transaction isolation. No exceptions are thrown and the transaction will always commit. I've also used the WITH (NOLOCK) hint on the select statement. If I leave out the table update it will work. If I leave out the assignment to a local variable and instead hard code a value it works. If I simply use the select where I would put the variable it will NOT work.
Interestingly, if I add some random select statement to the procedure it will return that result set. I can even select that same field from the same record I assign to my variable with no issue. But it still will not return my desired result set.
My result set is a select from a table variable which is populated via insert statements using variables set throughout the procedure. There are no table joins at all. I do pass 2 parameters to the procedure - one of which is used in my conditional statement in the original select. But I still get the same behavior when I omit both parameters and hard code values.
I have tried restarting my SQL Server (2005 version 9.0.4053), restarting my machine, i have tried with NOCOUNT ON and OFF, I'm basically out of ideas.
Edit - Details of VB call and stored procedure signature:
I'll try to give as good as a description as I can without publishing actual code. I'm actually posting this for another developer who works with me, so please bear with me.
VB6 Call:
With cmdCommand
.ActiveConnection = cnnConn
.CommandType = adCmdStoredProc
.CommandText = "uspMyStoredProcedure"
.Parameters("#strParam1") = strFunctionParameter1
.Parameters("#bolParam2") = bolFunctionParameter2
.Execute
End With
MyResultSet.CursorLocation = adUseClient
MyResultSet.Open cmdCommand, , adOpenStatic, adLockReadOnly
Stored Procedure signature:
CREATE PROCEDURE uspMyStoredProcedure
#strParam1 NVARCHAR(XX),
#bolParam2 BIT
AS
BEGIN
SET NO COUNT ON
DECLARE #var1 NVARCHAR(XX),
#var2 NVARCHAR(XX),
#var3 NVARCHAR(XX),
#var4 INT,
#var5 BIT
--DECLARATION OF OTHER VARIABLES
DECLARE #varTableVariable TABLE
(
strTblVar1 NVARCHAR(XX) ,
intTblVar2 INT ,
strTblVar3 NVARCHAR(XX) ,
bolTblVar4 BIT ,
datTblVar5 DATETIME
)
SELECT #var1 = t.Field1, #var2 = t.Field2
FROM Table1 t
WHERE t.ID = #strParam1
SELECT #var3 = t2.Field1
FROM Table2 t2
IF (Condition)
BEGIN
SET #var4 = 1
IF (Condition)
BEGIN
--SET SOME VARIABLES
END
ELSE
BEGIN
UPDATE TABLE1
SET Field3 = #var4
WHERE Field1 = #strParam1
END
END
ELSE
BEGIN
IF(Condition)
BEGIN
SELECT #var5 = ISNULL(Condition)
FROM Table3 t3
WHERE t3.Field = #strParam1
--SET SOME MORE VARIABLES
END
END
IF(Condition)
BEGIN
UPDATE Table1
SET Field5 = #SomeVariable
WHERE Field1 = #strParam1
END
INSERT INTO Table4 (Field1, Field2, Field3)
SELECT #SomeVar1, #someVar2, #SomeVar3
FROM SomeOtherTable
WHERE Field3 = #someVariable
IF(Condition)
BEGIN
INSERT INTO #varTableVariable (strTblVar1, intTblVar2,
strTblVar3, bolTblVar4, datTblVar5 )
VALUES (#SomeVar1, #SomeVar2, #SomeVar3, #SomeVar4, #SomeVar5)
END
SELECT *
FROM #varTableVariable
END
So, essentially, the procedure takes two parameters. It carries out a number of simple operations - inserting and selecting data from a couple of different tables, an update to a table and inserting a row into a table variable.
The procedure finishes with a select from the table variable. There's nothing fancy about the procedure, or the call from VB6. As previously stated, the behaviour observed is unusual in that by commenting out certain sections the call and return will work - data is returned. Calling the same procedure from a C#.NET test app works and successfully returns the desired result.
All we manage to get back in the VB6 app is an empty recordset.
Edit 2: We've just found out that if we create an arbitrary table to hold the data to be returned by the final select statement instead of using a table variable, the procedure works...
We discovered that the stored procedure was actually executing twice, due to the way it was being called from VB6:
With cmdCommand
.ActiveConnection = cnnConn
.CommandType = adCmdStoredProc
.CommandText = "uspMyStoredProcedure"
.Parameters("#strParam1") = strFunctionParameter1
.Parameters("#bolParam2") = bolFunctionParameter2
.Execute
End With
MyResultSet.CursorLocation = adUseClient
MyResultSet.Open cmdCommand, , adOpenStatic, adLockReadOnly
The command object 'cmdCommand' is executed the first time with the explicit call as the final line in the 'With' statement, '.Execute'.
What we found was that the last line: 'MyResultSet.Open cmdCommand...' is also implicitly executing the stored procedure a second time.
Since the stored procedure's function is essentially to activate and deactivate an alarm, by executing twice we were getting the activation and deactivation occurring at once and therefore no resultset returned.
Hopefully this might help avoid someone else getting stuck on something like this.

How to stop inserting records in SQL table when a limit is reached

I am using VB.net and SQL Server 2005.
My Problem is that I want to give user a message if the delegate limit has reached for that course.
See I have a course where I have MinDelegate and MaxDelegate limit. I want to stop inserting and give user message that "Max Delegate limit has reached can't have more delegates for this course"
And below is the insert commmand which is inserting records in my delegate table.
ALTER PROCEDURE [dbo].[uspInsertDelegate]
(
#CourseID int,
#CPUserID int,
#StatusID int,
#CreateUser varchar(25),
#CourseDate smalldatetime
)
AS
SET NOCOUNT OFF;
IF NOT EXISTS (SELECT *
FROM tblDelegate
WHERE CourseID = #CourseID and CPUserID = #CPUserID)
BEGIN
INSERT INTO tblDelegate
(
CourseID,
CPUserID,
StatusID,
CreateUser
)
VALUES
(
#CourseID,
#CPUserID,
#StatusID,
#CreateUser
)
END
UPDATE tblTraining
SET
TrainingDT = #CourseDate,
TrainingCompleted = #StatusID
WHERE CPUserID = #CPUserID
RETURN
Please also suggest what do in VB.NET code!
Thanks!
Best Regards,
Yuv
A solution is to write a "before INSERT" trigger which will enforce the business rules by checking how many students are readily registered for the class etc.) and prevent the insert to take place.
This error condition can then be detected at higher levels, and a proper error message provided to the user.
In re-reading your question, the business rule could as well be checked in the stored procedure you have in place, it would then just be a matter of making the store procedure return a condition code (say an integer: 0 = insert ok and -1 = class is full), and to test this value at the level of the application.
Edit: more details (but not quite the full code...)
Yuvraj, this looks seriously like homework, so I'd like to put you on the right track but also let you work at it enough that you learn the process of figuring things out.
With regards to the Store Procedure (SP), bniwredyc readily provided you the code:
It is a slight modification compared with what you have in your question:
#minDelegate int,
#maxDelegate int
set #delegatesCount = (select count(*) from tblDelegate
where CourseID = #CourseId)
if (#delegatesCount >= maxDelegate)
return -1
Essentially you add 2 extra arguments to the procedure: minDelegate and maxDelegate and return prematurely from the procedure, with a -1 return value, in case there are too many delegates. (1 in bniwredyc 's example, but I prefer negative values for error conditions). I don't think minDelegate is used at all; you know better which rules have to applied...
Now, you need to write a VB program which will invoke this SP by way of ADO. This will involve using the ADODB.Command object This Microsoft MSDN page provides reference info about this object and following a few links not too far from this page, you'll also find details about the Connection object and the RecordSet Object.
There are 3 ways that a stored procedure can return some data to the calling method.
1. By returning an integer value in the RETURN value. This value is
the return value of the Command object's Execute() method.
This is the simpler approach and can be used in your case
2. By returning values (integer, text or other) in one or several OUTPUT
Parameters
3. By returning a recordset
Method 2 can be combined with 1 or 3, however 1 and 3 are mutually
exclusive since they both use return value of the Execute() method
to provide an integer (1) or a Recordset (3).
The example at this page shows +/- all that you will need, but it uses a recordset for the data, which is not necessary in your case; Instead use an integer value for storing the return value of Execute(), and test it. If 0 : Record was added ok, If-1 : Failed the "too many" test.
Now, get to work :-) and please do tag your questions as "Homework" when appropriate.
You could create a validation stored procedure that just returns the total count of records. Call this first from your VB code then you check that count and then return the appropriate error message orelse call the insert sproc your provided.
You can pass a variable to the Stored Procedure with the MaxDelegates and do the check inside the Stored Procedure before inserting
Declare #CurrentNumberOfDelegates int
Select #CurrentNumberOfDelegates = Count(*)
From tblDelegate
Where CourseId = #CourseId
If #CurrentNumberOfDelegates > #MaxDelegates
Return -1
In this case you will check on VB.Net the return value of the stored procedure and if it is -1 show the message to the user.
This solution should be safe enough because you check the count just before you insert, but you may need to add a transaction to ensure that the limit is never passed by another thread running at the same time.
I think you can use this code for stored procedure:
ALTER PROCEDURE [dbo].[uspInsertDelegate]
(
#CourseID int,
#CPUserID int,
#StatusID int,
#CreateUser varchar(25),
#CourseDate smalldatetime,
#minDelegate int,
#maxDelegate int
)
AS
SET NOCOUNT OFF;
IF NOT EXISTS (SELECT *
FROM tblDelegate
WHERE CourseID = #CourseID and CPUserID = #CPUserID)
BEGIN
set #delegatesCount = (select count(*) from tblDelegate where CourseID = #CourseId)
if (#delegatesCount >= maxDelegate)
return 1
else
begin
INSERT INTO tblDelegate
(
CourseID,
CPUserID,
StatusID,
CreateUser
)
VALUES
(
#CourseID,
#CPUserID,
#StatusID,
#CreateUser
)
end
END
UPDATE tblTraining
SET
TrainingDT = #CourseDate,
TrainingCompleted = #StatusID
WHERE CPUserID = #CPUserID
RETURN 0
In VB code just check value that returned by execution of stored procedure: if it's 1 than max delegate limit has reached. You can also add some code to stored procedure to return value 2 in case of min limit have reached.
First of all thanks to all members who responded my Question
I solve this problem using below logic
In Sql Procedure I changed my procedure.
ALTER PROCEDURE [dbo].[uspInsertDelegate]
(
#CourseID int,
#CPUserID int,
#StatusID int,
#CreateUser varchar(25),
#CourseDate smalldatetime,
#MaxDelegate int
)
AS
SET NOCOUNT OFF;
IF NOT EXISTS (SELECT * FROM tblDelegate WHERE CourseID = #CourseID and CPUserID = #CPUserID)
BEGIN
Declare #DelegateBooked int
set #DelegateBooked = (SELECT count(*) FROM tblDelegate WHERE CourseID = #CourseID)
IF #DelegateBooked >= #MaxDelegate
SELECT 1
ELSE
BEGIN
INSERT INTO tblDelegate
(
CourseID,
CPUserID,
StatusID,
CreateUser
)
VALUES
(
#CourseID,
#CPUserID,
#StatusID,
#CreateUser
)
UPDATE tblTraining
SET
TrainingDT = #CourseDate,
TrainingCompleted = #StatusID
WHERE CPUserID = #CPUserID
END
END
And in my VB.net code I write:
Protected Sub btnSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSave.Click
If Not Session("CourseDate") Is Nothing Then
Try
Dim conString As String = WebConfigurationManager.ConnectionStrings("LocalSqlServer").ConnectionString
Dim con As New SqlConnection(conString)
con.Open()
Dim cmd As SqlCommand
For Each curRow As GridViewRow In GridView1.Rows
Dim chkSelect As CheckBox = CType(curRow.Cells(1).FindControl("chkSelect"), CheckBox)
If chkSelect.Checked Then
cmd = New SqlCommand("uspInsertDelegate", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add("#CourseID", SqlDbType.Int).Value = Session("CourseID")
cmd.Parameters.Add("#CPUserID", SqlDbType.Int).Value = CType(curRow.Cells(1).FindControl("lblCPUserID"), Label).Text
cmd.Parameters.Add("#StatusID", SqlDbType.Int).Value = 25
cmd.Parameters.Add("#CreateUser", SqlDbType.VarChar).Value = Session("LoggedInUser")
cmd.Parameters.Add("#CourseDate", SqlDbType.DateTime).Value = Session("CourseDate")
cmd.Parameters.Add("#MaxDelegate", SqlDbType.Int).Value = Session("MaxDelegate")
Dim retValue As Integer = CType(cmd.ExecuteScalar(), Integer)
If retValue = 1 Then
lblError.Visible = True
lblError.Text = "Max Delegate limit has reached can't have more delegates for this course"
Exit For
Else
lblError.Visible = False
End If
End If
Next
GridView1.DataBind()
Catch ex As Exception
ErrorHandler.WriteError(ex.Message)
End Try
End If
Please have a look and provide your feedbacks in case it is wrong