I have an app that runs a stored procedure in SQL Server, checking the difference of times between one row in a table and getdate()
I call this stored procedure from c#, and use a #returnValue to do some things
This is the method
public static bool Check(string CheckStored)
{
using (DbCommand command = DatabaseDA.DefaultDb.GetStoredProcCommand(CheckStored))
{
DatabaseDA.DefaultDb.AddParameter(command, "ReturnValue", DbType.Boolean, ParameterDirection.ReturnValue, "", DataRowVersion.Current, 0);
DatabaseDA.DefaultDb.ExecuteNonQuery(command);
return Convert.ToBoolean(command.Parameters["#ReturnValue"].Value);
}
}
and this is the call
bool notifNeeded = NotificationsDA.Check("CheckLastEnvioListadoComprobanteEjercicio");
then in SQL Server I have:
ALTER Procedure [dbo].[CheckLastEnvioListadoComprobanteEjercicio]
as
Begin
declare #UltimoEnvio datetime
declare #ReturnValue bit
select #UltimoEnvio = LastDate from EnvioListadoEjercicioComprobantes
Select #returnValue = CASE
WHEN DATEDIFF(hour, #UltimoEnvio, getdate()) >= 1 THEN 1
ELSE 0
END
from rep_inboxRequest
if (#ReturnValue = 1)
update EnvioListadoEjercicioComprobantes set LastDate = getdate()
return #ReturnValue
END
in EnvioListadoEjercicioComprobantes I have a row with the lastDate of one action (ej sending mail).
Right now, I have only one row, with 2012-06-18 06:40:02.210 value. I compare this date with the actual date, and return a bit if the difference is more than an hour.
Right now, in Argentina, its about 2012-06-18 11:26
If I execute getdate() in SQL Server, I get 2012-06-18 11:30:44.027
If I run my entire stored procedure in SQL Server and print #ReturnValue, I get 1 and the row is updated-
But when I call my stored from C#, I always get 0, and of course, the row didn't update.
What am I doing wrong ?
Why are you doing all that work (declare, select, update conditionally) when you can just perform a conditional update?
ALTER PROCEDURE [dbo].[CheckLastEnvioListadoComprobanteEjercicio]
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.EnvioListadoEjercicioComprobantes
SET LastDate = GETDATE()
WHERE LastDate <= DATEADD(HOUR, -1, GETDATE());
IF ##ROWCOUNT > 0
RETURN 1;
ELSE
RETURN 0;
END
GO
Anyway I don't think that's the way you deal with a return value. Try something like this (this is pseudo-code):
DatabaseDA.DefaultDb.ExecuteNonQuery(command);
var returnVal = command.Parameters.Add("#ReturnValue", SqlDbType.Int);
returnVal.Direction = ParameterDirection.ReturnValue; // this is important
DatabaseDA.DefaultDb.ExecuteNonQuery(command);
return Convert.ToBoolean(returnVal.Value);
Otherwise I suggest you stop using a return value for this, if you want the return parameter to be bit coming out of SQL Server, you can use an output parameter.
ALTER PROCEDURE [dbo].[CheckLastEnvioListadoComprobanteEjercicio]
#ReturnVal BIT = 0 OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.EnvioListadoEjercicioComprobantes
SET LastDate = GETDATE()
WHERE LastDate <= DATEADD(HOUR, -1, GETDATE());
SELECT #ReturnVal = ##ROWCOUNT;
END
GO
Now in your C# code (again, this is pseudo-code, I don't know that it will magically compile in your app if you copy and paste, but it should give you the idea):
SqlParameter rv = new SqlParameter("#ReturnVal", SqlDbType.Boolean);
rv.Direction=ParameterDirection.Output;
command.Parameters.Add(rv);
command.ExecuteNonQuery();
return Convert.ToBoolean(rv.Value);
Check your database's collation settings: In your Select statement, you are setting #returnValue but later you're checking and returning #ReturnValue. If case sensitivity is on, those are two different variables.
Try adding SET NOCOUNT ON at the top of your stored procedure , traditionally the return value is used to report the number of rows affected using the "SET NOCOUNT ON" option disables this feature which may be causing trouble with your return value.
Also check the SQL Server Logins Language option as this too can affect the way DateTime is calculated (amongst other things like collation). It might be that in Management Studio you are connecting using a windows account using argentinian local time but the C# application is connecting using a US/English login setting which will cause SQL Server to think you're in a different timezone.
Related
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).
I have a problem with a procedure that I'm developing in MySQL, this is my first time. A few months ago I made a small CRUD (select, insert, update) in MSSQL, and only sends the required data.
create procedure sp_Bodegas (#Opcion varchar(10), #CodBodega int = null, #NomBodega varchar(75) = null, #DirBodega varchar(150) = null, #EstBodega bit = null)
as
begin
set nocount on
if (#Opcion='SELECT')
begin
select cod_Bodega as CodBodega, nom_Bodega as NomBodega, dir_Bodega as DirBodega, est_Bodega as EstBodega from inv_Bodegas
end
if (#Opcion='INSERT')
begin
insert into inv_Bodegas (cod_Bodega, nom_Bodega, dir_Bodega, est_Bodega) values (#CodBodega, #NomBodega, #DirBodega, #EstBodega)
end
if (#Opcion='UPDATE')
begin
update inv_Bodegas set nom_Bodega = #NomBodega, dir_Bodega = #DirBodega where cod_Bodega = #CodBodega
end
set nocount off
end;
But a few days ago I started to work with MySQL and try to do the same procedure but this can not use variables such as '#NomBodega = Null' for not sending any data required option. then create input variables to receive the data, but now there are many parameters that have to send every time you run the procedure.
create procedure sp_Bodegas (in Opcion varchar(10), in CodBodega int, in NomBodega varchar(75), in DirBodega varchar(150), in EstBodega bit)
begin
if Opcion = 'SELECT' then
select cod_Bodega as CodBodega, nom_Bodega as NomBodega, dir_Bodega as DirBodega, est_Bodega as EstBodega from inv_Bodegas;
end if;
if Opcion = 'INSERT' then
insert into inv_Bodegas (cod_Bodega, nom_Bodega, dir_Bodega, est_Bodega) values (#CodBodega, #NomBodega, #DirBodega, #EstBodega);
end if;
if Opcion = 'UPDATE' then
update inv_Bodegas set nom_Bodega = #NomBodega, dir_Bodega = #DirBodega where cod_Bodega = #CodBodega;
end if;
end;
Wanted to know how I can make it work so very similar in my application, I am using aspx and EF for my database.
Thanks.
You have declared the variable as NomBodega but when you are using it in your procedure you use #NomBodega.
The #NomBodega would only be required if you passed in an out parameter to select the value after the procedure has run.
Thanks for your help, but i was very busy.
After read a lot of SP MYSQL i find is not possible so. i gona send every value.
Is it possible to have a default parameter for a mysql stored procedure?
I am trying to retrieve data from my database procedure but I'm not sure why I'm getting a return value of -1 when a value of 1 or 0 is supposed to be returned. I feel as thought everything is being passed in correctly and the procedure is running well. But I could be wrong.
I pass an object with the name of "Status" of type int into my database procedure db.proc_CsStatus and db.proc_GhStatus. During the debugger though I get "r" a value of -1.
My Controller:
public ActionResult Index()
{
ViewBag.CsStatus = GhCsStatusProvider.GetCsStatus();
ViewBag.GhStatus = GhCsStatusProvider.GetGhStatus();
return View();
}
My provider has these two functions:
public static int GetGhStatus()
{
using (Entities db = new Entities())
{
System.Data.Objects.ObjectParameter s = new System.Data.Objects.ObjectParameter("Status", typeof(int));
int r = db.proc_CsStatus(120, s);
return r;
}
}
public static int GetCsStatus()
{
using (Entities db = new Entities())
{
System.Data.Objects.ObjectParameter s = new System.Data.Objects.ObjectParameter("Status", typeof(int));
int r = db.proc_CsStatus(120, s);
return r;
}
}
Here are my database procedures:
For proc_GhStatus
USE [DATABASE_GH]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[proc_GhStatus]
-- Add the parameters for the stored procedure here
#TimeLimit Int,
#Status Int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- Declare variables.
DECLARE #LastUpdate Int
-- Calculate the LastUpdate.
SELECT #LastUpdate = DATEDIFF(second, Timestamp, CURRENT_TIMESTAMP)
FROM Heartbeat
WHERE Id=2
-- Compare it to the TimeLimit.
IF #LastUpdate > #TimeLimit SELECT #Status = 0
ELSE SELECT #Status = 1
END
GO
For CsStatus
USE [DATABASE_CS]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[proc_CsStatus]
-- Add the parameters for the stored procedure here
#TimeLimit Int,
#Status Int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- Declare variables.
DECLARE #LastUpdate Int
-- Calculate the LastUpdate.
SELECT #LastUpdate = DATEDIFF(second, Timestamp, CURRENT_TIMESTAMP)
FROM Heartbeat
WHERE Id=1
-- Compare it to the TimeLimit.
IF #LastUpdate > #TimeLimit SELECT #Status = 0
ELSE SELECT #Status = 1
END
GO
In the stored procedure you are not return the status. Therefore you can't set the status in the code like below.
int r = db.proc_CsStatus(120, s);
This will only return the status of the stored procedure execution. -1 means there were some errors in stored procedure execution. Simplest way would be remove the out parameter and return the #status from sp. Then you should be able to retrieve the values the way you have done.
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- Declare variables.
DECLARE #LastUpdate Int
-- Calculate the LastUpdate.
SELECT #LastUpdate = DATEDIFF(second, Timestamp, CURRENT_TIMESTAMP)
FROM Heartbeat
WHERE Id=1
-- Compare it to the TimeLimit.
IF #LastUpdate > #TimeLimit SELECT #Status = 0
ELSE SELECT #Status = 1
// Below line shoud be added.
RETURN #Status
Also make sure to set right return type in the DBModel as mentioned in other answers.
The possible problem is that the imported SP in EF is not told to return a value. To check do the followings:
Open the .edmx file.
Open Model Browser.
Brows to DBModel.
Under Function Imports section find your SP and right click on it then open Properties.
Check whether the Return Type is set to None or not.
If it is not change it to appropriate value.
I am trying to get the return value of a stored procedure. Here is an example of such a stored procedure:
select
Name,
IsEnabled
from
dbo.something
where
ID = #ID
if ##rowcount = 0
return 1
return
This is a simple select. If 0 rows are found, my result set will be null, but I will still have a return value.
This is a bad example, as this is a select, so sure I could find if 0 rows were returned. However, on an Insert, delete, or other calls, we need this return value to know if there was a problem. I have been unable to find a way to get this return value. I can get output values, I can get result sets, but no return value.
I can get the return value if I call SQL manually, or even if I run a SqlCommand using the Entity Framework, but this is not what I want to do.
Has anyone ever been able to get the return value from a stored procedure using Entity Framework?
Thanks for the help!
I guess support of stored procedure return values depends on version of Entity framework. Although directly returning value didn't work for me I managed to get value using OUTPUT stored procedure parameter. Example:
USE [YOUR_DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[TestReturnValue]
#InputValue int = 0,
#Result int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT #Result = #InputValue
RETURN(#InputValue);
END
This is test code:
void TestReturnValue()
{
using (var db = new YourDBContext())
{
var result = new System.Data.Objects.ObjectParameter("Result", 0);
Console.WriteLine("SP returned {0}", db.TestReturnValue(123, result));
Console.WriteLine("Output param {0}", result.Value);
}
}
And this is output:
SP returned -1
Output param 123
As you see directly returned value output some rubbish but OUTPUT parameters works!
This article provides general info on two ways of returning values from SP
Hope this helps
No. Entity Framework doesn't have rich stored procedure support because its an ORM, not a SQL replacement.
As you have already discovered, if you need to use less common (more advanced?) features of stored procedures, you'll have to use good old fashioned ADO.NET.
You must use the "Context.Database.SqlQuery",and specify the output type of the stored procedure
var spReturnVaue = Context.Database.SqlQuery<Type>("[schema].YourSp").FirstOrDefault();
int firstId = Context.Database.SqlQuery<int>("[dbo].GetFirstId").FirstOrDefault();
Based on Hadi Salehy's answer, I wrote this code and works for me.
My SP
CREATE PROCEDURE [schema].[ProcedureName]
#param1 int,
#param2 varchar(20),
#param3 varchar(50)
AS
UPDATE Table1
SET Field2 = #param2, Fiedl3 = #param3
WHERE (Field1 = #param1)
SELECT ##ROWCOUNT;
My C# code.
int quantity = await _context.Database.ExecuteSqlRawAsync("[schema].[ProcedureName] {0}, {1}, {2}", myModel.Param1, myModel.Param2, myModel.Param3);
In order to get the interested result in EF, you should return a same structure (not 1 and Name, IsEnabled)
In this query you should return '',0 for example instead of 1 in the if condition:
select
Name,
IsEnabled
from
dbo.something
where
ID = #ID
if ##rowcount = 0
return '',0
I wrote this function and now its always returning true...
can anybody help me?
Create FUNCTION dbo.ValidateBranch
(
#BranchID nvarchar(50),
#Password nvarchar(50)
)
RETURNS bit
AS
BEGIN
declare #Res bit
if exists(Select * From TPasswords Where (BranchID = #BranchID) and (isSecurity = 0) and (Pass = #Password))
set #Res = 1
else
set #Res = 0
RETURN #Res
END
and i'm callign like so:
bool isValidBranch = taQueries.ValidateBranch(IDBranch, PasswordTextBox.Text) ?? false;
The T-SQL code seems fine - how do you "always get back true" - in SQL Mgmt Studio, or from your app calling this function??
How are you calling this function, can you show us that piece of code??
I quickly recreated the setup and in my case, on SQL Server 2008 R2, it works just fine when I call this function like this:
SELECT dbo.ValidateCenter('Center1', 'Pwd1') -- return 1
SELECT dbo.ValidateCenter('Center1', 'Pwd1333') -- return 0