I have a stored procedure in Microsoft SQL Server that looks similar to this:
ALTER PROCEDURE [MySchema].[TestTable_MGR_RetrieveLaterThanDate]
#TestDate DATETIME,
#TableData CURSOR VARYING OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET #TableData = CURSOR FOR
SELECT *
FROM MySchema.TestTable
WHERE #TestDate <= test_date;
OPEN #TableData;
END
I need to call this from C#, but I have problems creating the SqlParameter object that is needed to hold the data of the output cursor.
The parameters I am creating look like this:
SqlParameter testDateParameter = new SqlParameter();
testDateParameter.ParameterName = "#TestDate";
testDateParameter.Direction = ParameterDirection.Input;
testDateParameter.SqlDbType = SqlDbType.DateTime;
testDateParameter.Value = theValue;
// I have no idea on what the correct SqlDbType should be here
SqlParameter tableDataParameter = new SqlParameter();
tableDataParameter.ParameterName = "#TableData";
tableDataParameter.Direction = ParameterDirection.Output;
tableDataParameter.SqlDbType = SqlDbType.???;
I have tried (for the cursor parameter) both SqlDbType.Udt and SqlDbType.Structured but in both cases, I couldn't get what I wanted when calling the ExecuteReader method of the SqlCommand (exceptions in both cases). I tried those two because I did not see any option for cursors.
I understand cursors are usually not encouraged, but does .NET not allow at all reading of cursors from SQL Server stored procedures, or is there something I am missing?
Thank you in advance for all the help.
Below is the relevant excerpt from the Return Data from a Stored Procedure documentation:
The cursor data type cannot be bound to application variables through
the database APIs such as OLE DB, ODBC, ADO, and DB-Library. Because
OUTPUT parameters must be bound before an application can execute a
procedure, procedures with cursor OUTPUT parameters cannot be called
from the database APIs. These procedures can be called from
Transact-SQL batches, procedures, or triggers only when the cursor
OUTPUT variable is assigned to a Transact-SQL local cursor variable.
Although the doc doesn't call out SqlClient specifically, the consideration applies to all SQL Server APIs. I believe the restriction is because the underlying SQL Server TDS protocol does not support it. ADO.NET providers for some other DBMS products (e.g. Oracle) do support cursor parameters.
Other SQL Server client APIs do have the notion of cursors but those are implemented via system API stored procedures rather than T-SQL, using server-side statement handles and client API methods to use them. SqlClient, OTOH, is designed to stream data back to the client instead of maintaining server-side cursor state.
Although I do not recommend this technique, you could avoid the cursor output parameter by declaring a T-SQL global cursor in one stored proc and calling another on the same connection for RBAR.
CREATE OR ALTER PROCEDURE dbo.TestTable_MGR_RetrieveLaterThanDate
#TestDate DATETIME
AS
SET NOCOUNT ON;
DECLARE TestTable_MGR_RetrieveLaterThanDate CURSOR GLOBAL FAST_FORWARD READ_ONLY
FOR SELECT * -- consider an explicit column list here
FROM MySchema.TestTable
WHERE #TestDate <= test_date;
OPEN TestTable_MGR_RetrieveLaterThanDate;
GO
CREATE OR ALTER PROCEDURE dbo.FetchNext_TestTable_MGR_RetrieveLaterThanDate
AS
SET NOCOUNT ON;
FETCH NEXT FROM TestTable_MGR_RetrieveLaterThanDate;
IF ##FETCH_STATUS <> 0
BEGIN
CLOSE TestTable_MGR_RetrieveLaterThanDate;
DEALLOCATE TestTable_MGR_RetrieveLaterThanDate;
RETURN ##FETCH_STATUS;
END;
GO
Ultimately, CURSOR doesn't like to be used like this in SQL Server, and while there are ways to use CURSOR in some scenarios, frankly it is almost never a good idea.
Since you are trying to use ExecuteReader with this, the logical conclusion is: just use SELECT:
ALTER PROCEDURE [MySchema].[TestTable_MGR_RetrieveLaterThanDate]
#TestDate DATETIME
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM MySchema.TestTable
WHERE #TestDate <= test_date;
END
This will work just fine with ExecuteReader.
I am currently trying to stabilize an asp.net 2.0 website.
I am about 95% sure that the main problem in the stability of the system is that the C# code is leaking SQL connections.
The accepted answer on this post describes exactly my problem:
Why is my SqlCommand returning a string when it should be an int?
That beign said, I am currently running this sql statement to pinpoint the possible problem:
SELECT S.spid, login_time, last_batch, status, hostname, program_name, cmd,
(
select text from sys.dm_exec_sql_text(S.sql_handle)
) as last_sql
FROM sys.sysprocesses S
where dbid > 0
and DB_NAME(dbid) = 'db'
and loginame = 'login'
order by last_batch asc
What I find weird is that the login used to connect to the DB from the website keeps returning last_sql as:
CREATE PROCEDURE name
-- Add the parameters for the stored procedure here
...
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
... Procedure code
The question is, why would a create procedure statement be run over and over?
Also, is it a bad practice to have 3-4 (not so active) website connecting to DB using the same connection string?
I have this stored procedure to retrieve data from a database (dynamic query). I am calling this stored procedure from C# code, passing two parameters to this stored procedure:
ALTER PROCEDURE [dbo].[GetCompleteCPTDetails]
#Practice_Short_Name varchar(50),
#Uploaded_Date nvarchar(max)
AS
BEGIN
DECLARE #CPTtablename nvarchar(300)
DECLARE #vQuery NVARCHAR(max)
DECLARE #upldate nvarchar(100)
SET #upldate = #Uploaded_Date
SET #CPTtablename = 'ACER_CLAIMS_MASTER_DETAIL_Hist_' + #Practice_Short_Name
SET #vQuery = 'SELECT Practice_Short_Name, Service_Date_From, Carrier_Name,
Location_Description, Patient_Number, Patient_First_Name,
Patient_Last_Name, Voucher_Number, Procedure_Code, Service_Fees,
Service_Payments, Service_Adjustments, Acer_Status, Acer_Allowed_Amount
FROM ' +#CPTtablename+'
WHERE Uploaded_Date =''' + #upldate + '''
ORDER BY acer_status ASC, Service_Date_From DESC, Patient_First_Name ASC'
EXEC (#vQuery)
END
But when I am running this query I get a timeout error. If I assign value to my parameters in the stored procedure and run it from query windows then it is showing correct data.
Can anyone please explain to me why I get a timeout error if I am calling it from C#?
That is a pretty simple where and order by.
Unless that is just a massive table with no indexes that should be fast.
Is there an index on Uploaded_Date and is it not fragmented.
Also an index on the sort would help.
Are you loading everything into a DataTable?
If so try loading into DataReader.
Try a top 1 and remove the order by.
If that does not return then you have connection issue as no way that query should time out.
The other thing to try is with (no lock) to see if it is a lock problem.
Why is #Uploaded_Date nvarchar(max)?
Is that a date or not?
There can be many solutions to this problem, as problem areas can be different in each case.
But most common:
Check & increase sqlcommand timeout in your application
Try calling this SP asynchronously
Also i would like to know, your application on the same machine where DB resides?
I have a stored procedure that contains dynamic select. Something like this:
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #query NVARCHAR(max)
SET #query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + #groupId
/* This actually contains a dynamic pivot select statement */
EXECUTE(#query);
END
In SSMS the stored procedure runs fine and shows result set.
In C# using Entity Framework it shows returning an int instead of IEnumerable?
private void LoadTestRecords()
{
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
string id = ddlGroupId.SelectedValue;
List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
GridView1.DataSource = list;
}
Generated function for usp_GetTestRecords
public virtual int usp_GetTestRecords(string groupId)
{
var groupIdParameter = groupId != null ?
new ObjectParameter("groupId", groupId) :
new ObjectParameter("groupId", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
I get this when I have a stored procedure that includes an "exec" call into a temporary table, such as:
insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0
It appears that Entity Framework gets confused as to what should be returned by the procedure. The solution I've come across is to add this at the top of the sproc:
SET FMTONLY OFF
After this, all is well.
I got the same problem, and found solution here
Move to your .edmx
At Model Browser Window/Function Imports find your procedure then double click it
Change the return type to you want
Save .edmx and check the return type again.
It should be what you need now.
Entity Framework can't tell what your stored procedure is returning. I've had success creating a table variable that mirrors the data from your SELECT statement. Just insert into the table variable then do a select from that table variable. EF should pick it up.
See Ladislav Mrnka's answer in this Stack Overflow post
https://stackoverflow.com/a/7131344/4318324
I had the same basic problem.
Adding
SET FMTONLY OFF
To a procedure you are trying to import during the import will address this problem.
It's a good practice to remove the line afterwards unless the purpose of the database is solely to provide schema for EF (Entity Framework).
The main reason for caution is that EF uses this setting to prevent data mutations when trying to obtain metadata.
If you refresh your entity model from a database any procedures with this line in them can potentially update the data in that database just by trying to obtain the schema.
I wanted to add a further note on this so it's not needed to fully scan through the other link.
if you want to try to use FMTONLY here are a couple things to keep in mind.
when FMTONLY is on:
1) only the schema is returned (no) rows.
similar to adding a blanket false statement to your where clause (ie "where 1=0")
2) flow control statements are ignored
Example
set fmtonly on
if 1=1
begin
select 1 a
end
else
begin
select 1 a,2 b
end
while 1=1
select 1 c
The above returns NO rows whatsoever and the metadata for each of the three queries
For this reason some people suggest toggling it off in a way that takes advantage of it's non-observance of flow control
if 1=0
begin
set fmtonly off
end
In fact you could use this to introduce logic that tracks this
set fmtonly off
declare #g varchar(30)
set #g = 'fmtonly was set to off'
if 1=0
begin
set fmtonly off
set #g = 'fmtonly was set to on'
end
select #g
Think VERY CAREFULLY before trying to use this feature as it is both deprecated and potentially makes sql extremely hard to follow
the MAIN concepts that need to be understood are the following
1. EF turns FMTONLY on to prevent MUTATING data from executing stored procedures
when it executes them during a model update.
(from which it follows)
2. setting FMTONLY off in any procedure that EF will attempt to do a schema scan
(potentially ANY and EACHONE) introduces the potential to mutate database
data whenever *anyone* attempts to update their database model.
Entity Framework will automatically return a scalar value if your stored procedure doesn't have a primary key in your result set. Thus, you'd have to include a primary key column in your select statement, or create a temp table with a primary key in order for Entity Framework to return a result set for your stored procedure.
I had the same problem, I changed the name of return fields by 'AS' keyword and addressed my problem. One reason for this problem is naming column names with SQL Server reserved keywords.
The example is fallows:
ALTER PROCEDURE [dbo].[usp_GetProducts]
AS
BEGIN
SET NOCOUNT ON;
SELECT
, p.Id
, p.Title
, p.Description AS 'Description'
FROM dbo.Products AS p
END
Best solution I found is to cheat a little.
In the store procedure, comment everything, put a first line with a select [foo]='', [bar]='' etc...
Now update the model, go to the mapped function, select complex type and click on Get Column Information and then Create Complex Type.
Now comment the fake select and un-comment the real store procedure body.
When you generated your model class for your stored procedure, you chose scalar return result by mistake. you should remove your stored procedure from your entity model, then re-add the stored procedure. In the dialog for the stored procedure, you can choose the return type you are expecting. Do not just edit the generated code.. this may work now, but the generated code can be replaced if you make other changes to your model.
I have pondered this a bit and I think I have a better/simpler answer
If you have a complex stored that gives entity framework some difficultly (for current versions of Entity Framework that are using the FMTONLY tag to aquire schema)
consider doing the folowing at the beginning of your stored procedure.
--where [columnlist] matches the schema you want EF to pick up for your stored procedure
if 1=0
begin
select
[columnlist]
from [table list and joins]
where 1=0
end
if you are okay loading your result set into a table variable
you can do the following to help keep your schema in sync
declare #tablevar as table
(
blah int
,moreblah varchar(20)
)
if 1=0
begin
select * from #tablevar
end
...
-- load data into #tablevar
select * from #tablevar
If you need to do this, then you might be better off just making a partial of the dbcontext and creating the C# function yourself that will use SqlQuery to return the data you need. Advantages over some of the other options is:
Don't have to change anything when the model updates
Won't get overwritten if you do it directly in the generated class (someone above mention this as if it's an option :) )
Don't have to add anything to the proc itself that could have side effects now or later on
Example Code:
public partial class myEntities
{
public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
{
// fill out params
SqlParameter p1 = new SqlParameter("#p1", _p1);
...
obj[] parameters = new object[] { p1, p2, groupId };
// call the proc
return this.Database.SqlQuery<MyClass>(#"EXECUTE usp_GetTestRecords #p1, #p2, #groupId", parameters).ToList();
}
}
Just change to
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM CUSTOMERS WHERE Id = #groupId
END
I know this is an old thread but in case someone has the same problems I'll tell my woes.
As a help to find the issue, run sql profiler when you add your stored proc. Then you can see what entity framework is passing as parameters to generate your resultset. I imagine nearly always it will pass null parameter values. If you are generating sql on the fly by concatenating string values and parameter values and some are null then the sql will break and you wont get a return set.
I haven't needed to generate temp tables or anything just an exec command.
Hope it helps
During import
SET FMTONLY ON
can be used for taking the sp schema.
If you change the sp and want to update the new one, you should delete the old defined function from edmx file (from xml), because although deleting sp from model browser, it is not deleted in edmx. For example;
<FunctionImport Name="GetInvoiceByNumber" ReturnType="Collection(Model.Invoice_Result)">
<Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
</FunctionImport>
I had the same problem, and when I delete the FuctionImport tag of corresponding sp totally, the model updated right. You can find the tag by searching the function name from visual studio.
You may have luck opening up the model browser, then going to Function Imports, double clicking the stored procedure in question and then manually clicking "Get Column Information" and then clicking "Create New Complex Type". This usually sorts out the problem.
Well I had this issue as well but after hours of online searching none of above methods helped.
Finally I got to know that It will happen if your store procedure is getting some parameters as null and which generate any error in query execution.
Entity Framework will generate method for store procedure by defining the complex entity model. Due to that null value your store procedure will return and int value.
Please check your store procedure either its providing empty result set with null values. It will fix your problem. Hopefully.
I think this is a problem of permissions on the database, I don't know what exactly could be, but, in my job we use Active Directory users to grant applications connect to databases, this accounts are specially created for the applications, each app has its own user account, well, as a developers I have permissions for read, write and other basic things, no alter, and no advanced features, and I have this same problem running Visual Studio with my normal account, then, what I did was to open Visual Studio selecting the option "as a different user" on the context menu, and I put the AD login granted for the application and voila!, now my Stored Procedures are loading with all the fields I was expected, before that, my Stored Procedures was returning as int. I hope this help someone, maybe the VIEW DEFINITION permissions on database account do the trick
If SQL Authentication is in place, verify that the user credential that is being used to connect Entity Framework to the database has the proper rights to read from CUSTOMERS table.
When Entity Framework uses SQL Authentication to map complex objects (i.e stored procedures that SELECTs more than one column), if any of the tables from within such stored procedure don't have set up the Read permission, the mapping will result in returning INT instead of the desired Result set.
We have the ability to execute stored procs from the middle-tier. Basically, in a database table called "SQLJobStep" we have -- among other things -- a varchar(300) column called "StoredProcedure" which holds something like this:
usp_SendReminderEmails #Debug=0, #LoginID
Via the middle-tier, a user clicks on a link to run the proc of their choice. What happens in the backend is a proc ("usp_SQLJobsActionGet") looks up the correct value from the "SQLJobStep.StoredProcedure" column and executes the value above.
This is the part of the code from "usp_SQLJobsActionGet" that executes the above value:
DECLARE #StepId int
DECLARE #LoginID varchar(12)
DECLARE #StoredProcedure varchar(300)
SET #StepId = 74
SET #LoginID = 'Yoav'
SELECT #StoredProcedure = SJS.StoredProcedure
FROM SQLJobStep AS SJS
WHERE SJS.StepId = #StepId
SET #StoredProcedure = ISNULL(#StoredProcedure, '')
IF CHARINDEX('#LoginID', #StoredProcedure) > 0
BEGIN
SET #LoginID = ISNULL(#LoginID, 'UNKNOWN')
SET #StoredProcedure = REPLACE(#StoredProcedure, '#LoginID', '#LoginID = ''' + #LoginID + '''')
END
IF #StoredProcedure != ''
BEGIN
EXEC(#StoredProcedure)
END
Fairly simple stuff....
The above code converts the original value to (and then executes):
usp_SendReminderEmails #Debug=0, #LoginID = 'Yoav'
Here is the issue:
When executing the "usp_SendReminderEmails #Debug=0, #LoginID = 'Yoav'" value nothing happens. No error is returned to the middle-tier. But I know that a value is pulled from the SQLJobStep table because we have other stored procedure values that get pulled and they run fine. (Note that the other values only have the #LoginID parameter, while this has #Debug=0 as well.)
At the same time, if I run the code above (both the gutted code and calling "usp_SQLJobsActionGet" directly) in SQL Management Studio, it works perfectly.
Do you have any advice? I am sure I am missing something very basic.
Thanks.
My advice? Use sp_ExecuteSQL instead of concatenation / replacement:
IF #StoredProcedure != ''
BEGIN
EXEC sp_ExecuteSQL #StoredProcedure, N'#LoginID varchar(12)', #LoginID
END
Overall, though - the EXEC should work; are you sure that #StoredProcedure is not empty?
Thanks for helping. I found the answer to my issue, and as you can guess it had to do with issues beyond what I described originally:
In the usp_SendReminderEmails proc, we call another proc in order to audit each e-mail record that is sent. This auditing proc inserts a record into an audit table and then returns the identity (SELECT TOP 1 SCOPE_IDENTITY()). While it only returns 1 record at a time, it happens to be called in a cursor (in usp_SendReminderEmails) to send out each email at a time (note: this is a SQL Job proc).
What I noticed is that upon executing usp_SendReminderEmails #Debug=0, #LoginID = 'Yoav' in Management Studio, it works fine but there is a warning returned(!):
The query has exceeded the maximum number of result sets that can be displayed in the results grid. Only the first 100 result sets are displayed in the grid.
When calling the proc from the middle-tier, therefore, nothing happened - no error returned, but no processing of usp_SendReminderEmails either. I fixed it by calling the audit proc in an insert into temp table in usp_SendReminderEmails, thereby ensuring it doesn't get returned (since it's only an identity value):
INSERT INTO #AuditData (AuditDataId)
EXEC usp_AuditSave