In Ado.net, the code is calling a stored procedure with input and output parameters.
I understand that if some of the input parameters are optional (have default values in the SP), the code doesn't need to define and send the parameters values unless needed to.
My question is:
Does the same apply to the optional output parameters? can the code ignore the optional (has a default value) SP output parameters?
I could have tested it myself but I don't have a working example right now, and I am short of time.
Thanks you.
Yes. If a parameter has a default value then it may be safely omitted, irrelevant of the parameter direction (INPUT or OUTPUT). The fact that the procedure is called from ADO.Net is entirely irrelevant. Eg:
create procedure usp_test
#a int = 1 output,
#b int = 2
as
begin
set #a = #b;
end
go
exec usp_test
Whether is safe to do from a business rules point of view (ie. ignoring an OUTPUT parameter returned value), is entirely up to the specifics of the procedure and your app.
EDIT: Turns out I was wrong here, but I'm going to leave my answer because the information on SqlParameter might be useful. Sorry for the inaccuracy though.
I don't believe so. You must send in an OUTPUT parameter and in ADO.NET this is accomplished by adding a SqlParameter with it's ParameterDirection property set to ParameterDirection.Output.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparameter.direction.aspx
http://msdn.microsoft.com/en-us/library/system.data.parameterdirection.aspx
Related
I'm dealing with an Oracle DB, connecting from go via InstantClient (version 11) (https://github.com/mattn/go-oci8). I need to be able to load this object and browse results... t_cursor output parameter.
I have tried many strategies, I know how to map function parameters to go structures but I don't know how to work with t_cursor type since it seems not being implemented in InstantClient
Example of stored procedure
create or replace procedure EXAMPLE(a IN NUMBER, b IN NUMBER, c OUT T_CURSOR) AS BEGIN
[Edit] We have also tried to execute SQL blocks from code to try to handle this third parameter.
i.e.
If you add something like
declare
c t_cursor;
begin
EXAMPLE(:1, :2, c)
end
then I don't know how you can get the block to return a result set that contains the cursor.
declare
c t_cursor;
begin
EXAMPLE(:1, :2, c)
select 1, c from dual
end
The whole block returning the result of that select would be ideal but oracle blocks do not return result sets afaik.
Anyone who can bear a hand on this?
Thank you very much
It can be done with the driver https://github.com/rana/ora instead.
*Rset may be passed to Stmt.Exe when prepared with a stored procedure accepting an OUT SYS_REFCURSOR
The README.me even has that exact example.
Caveats:
It's unclear whether the database/sql interface may be used or you are limited to the lib specific API.
Instant Client gets restricted to versions from 12.1.0.1.0 on.
I came across a stored procedure which does this:
DECLARE #DebugLogging BIT = dbo.fnIsDebugLoggingEnabled();
IF (#DebugLogging = 1)
BEGIN
-- some verbose logging information here...
END
based on whether a debug logging is enabled or not, which is an output given by scalar function dbo.fnIsDebugLoggingEnabled().
I checked the create script for that function and I think it will always return 0. Maybe I am wrong. Can someone check if I am thinking correctly or not?
I am thinking --> why would someone put efforts to do something for a condition that will never occur?
Script for the function is:
CREATE FUNCTION [dbo].[fnIsDebugLoggingEnabled]()
RETURNS BIT
AS
BEGIN
RETURN 0;
END
I ran select dbo.fnIsDebugLoggingEnabled() and it indeed returned 0.
Your observation is correct; the returned value will always be zero so the verbose logging will not occur.
I can't say why the developer chose to implement the debugging flag this way but it may be a deployment option where different versions of the function are created depending on the selection. Personally, I'd store configurable values like this in a table.
I've been converting an oracle schema to an sql server one and got the following error
Invalid use of a side-effecting operator 'SET COMMAND' within a function.
In my case modifying the database involved this
set #originalDateFirst = ##DateFirst;
set datefirst 1;
set #DayOfWeek = datepart(weekday,#DATE); -- 1 to 5 = Weekday
set datefirst originalDateFirst;
Ideally this wouldn't have modified the database but the datepart function uses static state.
I'm not really from a database background so was slightly baffled by this but reading other answers it looked like all I needed to do was swap the word function for procedure and I'd be away. However I then got the following error
Incorrect syntax near 'RETURNS'.
Reading around a bit about stored procedures aren't allowed to return anything they like - only integers. However the integers normally have the same semantics as a console application's return code - 0 is success and anything else is an error.
Luckily the type I wanted to return was an integer so fixing the next error:
Incorrect syntax near 'RETURNS'.
Involved just removing
RETURNS INTEGER
from the function/procedure. However I'm unsure if there are any weird side effects caused by this error code interpretation that will be outside of my control. The function actually just returns either 0 or 1 basically as a true or false flag (where 1 is true and 0 is false as you might expect). Therefore one of my return values would count as an 'error'.
What if any are the consequences of piggybacking on the return code of a procedure rather than using an out parameter? Is it just a bad practice? If it's safe to do this I'd certainly prefer to so I don't need to change any calling code.
This isn't an answer to your question as posed, but may be a better solution to the overall problem.
Rather than having to rely on a particular DATEFIRST setting, or changing the DATEFIRST setting, why not use an expression that always returns reliable results no matter what the DATEFIRST setting is.
For example, this expression:
select (DATEPART(weekday,GETDATE()) + 7 - DATEPART(weekday,'20140406')) % 7
always returns 1 on Mondays, 2 on Tuesdays, ..., 5 on Fridays. No matter what settings are in effect.
So, your entire original block of 4 lines of code could just be:
set #DayOfWeek = (DATEPART(weekday,#Date) + 7 -
DATEPART(weekday,'20140406')) % 7; -- 1 to 5 = Weekday
And now you should be able to continue writing it as a function rather than a stored procedure.
If it's safe to do this I'd certainly prefer to so I don't need to change any calling code.
Which you would have to do if you did change your function into a stored procedure. There's no syntax where you can look at the call and ever be in doubt of whether a stored procedure or a function is being invoked - they always use different syntaxes. A procedure is executed by being the first piece of text in a batch or by being preceded by the EXEC keyword and no parentheses.
A function, on the other hand, always has to have parentheses applied when calling it, and must appear as an expression within a larger statement (such as SELECT). You cannot EXEC a function, nor call one by it being the first piece of text in a batch.
An out param could be of (almost) any valid datatype, RETURN is always an int, not necessarily 0 or 1.
Because you can't use a procedure as a query source (it's not a table), to consume a return value from a procedure, declare a variable and exec the procedure like this:
create procedure p as
-- some code
return 13
go
declare #r int
exec #r = p
select #r
I wouldn't call it piggybacking, it's a regular way to return a success/error code for example. But how you interprete the return value is entirely up to calling code.
Functions, otoh, can be used as a query source, if table-valued, or as a scalar value in select list or where clause etc. But you can't modify data inside functions, and there are other restrictions with them (as you've learned already). Furthermore, functions can have nasty impact on performance (except the inline table-valued functions, they're pretty much safe to use).
I'm creating a SQL Server unit test using tSQLt.
The proc that I'm testing returns 3 result sets. My webAPI handles the multiple result sets and sends it to the UI fine.
Question: In my SQL Server unit test, how do I handle the 3 result sets? If the proc returns one result set, it is easy to handle. I use the following:
Insert Into #ReturnData
(
ID,
Data1,
Data2
)
Exec #Ret = StoreProcName
Then I can run a bunch of checks against the #ReturnData temp table. But I don't understand how to handle/test a proc if it returns multiple result sets. Is this even possible?
Thanks.
The method I'd suggest you use is tSQLt.ResultSetFilter(). This takes a parameter for number of the result set to return and calls your code under test (StoreProcName in your example), returning that result set, which you can then use Insert..Exec to capture.
The down side of this procedure is that it only captures that one result set per run - so you need to call it multiple times to return all of the result sets. I usually only look at one result set per test, allowing me to concentrate on answering one question in that test, but if your result sets interrelate and you need both to return for your test to be evaluated, then you will need to call tSQLt.ResultSetFilter and hence the code under test more than once in your test (the manual has more info on this situation)
As an aside, I have previously blogged about some unexpected behaviour I encountered when using insert..exec with SPs that return multiple identical result sets which may be of interest.
DaveGreen has the answer. But for completeness, I wanted to share this which expands on the basics: http://tsqlt.org/201/using-tsqlt-resultsetfilter/
If you call a stored procedure and need to pass in parameters, do the following:
Create a #Variable that holds the ‘exec …’ string with the parameter values embedded. Then you can do something like this:
Declare #Variable Varchar(max)
Set #Variable = ‘exec STOREDPROCNAME ‘’param1’’, ‘’param2’’’;
EXEC tSQLt.ResultSetFilter 2, #Variable
The number 2 specifies the second result set that is returned.
Nice and snappy ... ;-)
There's a stored procedure with the parameter that may take default value
CREATE PROCEDURE [dbo].[FooSP]
#FooParam INT = 15
AS
So is there a way to determine whether that SP was called with or without setting the #FooParam value.
IOW, is there a way to determine if the user called
exec dbo.FooSP(15)
or
exec dbo.FooSP()
Thanks!
If you need to do that, you are usually better off changing the default value to some kind of sentinal value (NULL often works well in db stored procedures,) and putting some logic in the procedure to set the value to you old default (15 in this case) if NULL is received.
That way, you get the benefit of a default value, and the ability to know what was passed in.
UPDATE
If there is no possible sentinal value -- say, because the parameter is very constrained, where all possible inputs are legitimate, then you will have to either do some stack introspection, or change the function signature.
If you can change the calling code, then make the procedure with the default parameter actually a different procedure, which then calls your original procedure with the default set explicitly.
You might want to vote for this related Connect item - getting call stack information can be really useful to be able to instrument code, and debug situations.