I have this procedure that executes another procedure passed by a parameter and its parameters datefrom and dateto.
CREATE procedure [dbo].[execute_proc]
#procs varchar(200),
#pdatefrom date,
#pdateto date
as
exec #procs #datefrom=#pdatefrom,#dateto=#pdateto
But I need to also pass the parameters dynamically without the need to edit them in the procedure. For example, what I am imagining is something like this
CREATE procedure [dbo].[execute_proc]
#procs varchar(200),
#params varchar(max)
as
exec #procs #params
where #params is a string like #param1=1,#param2='somethingelse'
Is there a way to do this?
It's not really clear what the point of your wrapper procedure is (auditing? debugging?), and it seems like a very awkward solution. If you explain why you want to do this, someone may have a completely different and hopefully better solution.
The biggest issue with your proposal is that you can only pass parameters as strings and that means you have to handle all the escaping, data conversion/formatting and SQL injection issues that come with dynamic SQL. It would be much better to call each procedure directly, passing correctly typed parameters from your calling code.
Having said all that, if you really want to do it then you can do something like this:
create proc dbo.ExecuteProcedure
#ProcedureName sysname,
#Parameters nvarchar(max),
#Debug bit = 0x0,
#Execute bit = 0x1
as
set nocount on
begin
declare #sql nvarchar(max)
set #sql = 'exec ' + quotename(#ProcedureName) + ' ' + #Parameters
if #Debug = 0x1 print #sql
if #Execute = 0x1 exec(#sql)
end
go
exec dbo.ExecuteProcedure 'dbo.SomeProc', '#p1 = 1, #p2 = ''themhz''s proc''', 0x1, 0x0
You should also have a look at sp_executesql, which does almost exactly what you want, but it needs to have all the parameter data types too, which you say is not possible in your scenario.
Put the stored procedure name in a varchar field in your client table
Retrieve the SP name and assign it to a parameter ( spName) when the client is chosen.
In code create a function that returns a string
function PassStoredProcedureName(spName as string) as string
return spName
end function
Set your dataset to "Stored Procedure"
Open a dataset Expression window
Enter =Code.PassStoredProcedureName(Parameters!spName.value)
When you chose a client, the spName will be assigned to the parameter. When the dataset executes, it will pass the parameter to the function, which will pass the spName to the dataset.
I use this to execute custom stored procedures for clients when the same stored procedure will not work for all clients.
Be sure to normalize the aliased field names so that data retrieval to a report does not break.
Your stored procedures should always have the same parameter requirements even if they are not needed.
Related
i am trying to execute this scalar function and i tried a lot of approaches to achieve this but i get stuck
Create FUNCTION CalculateElementFunc()
RETURNS int
AS
BEGIN
DECLARE #ResultVar numeric(18,6)
DECLARE #eq nvarchar(MAX)
set #eq = '7.5/100*1258.236'
declare #expression nvarchar(max)
set #expression = #eq
declare #result int
declare #SQLString nvarchar(max)
Set #SQLString = N'Select #result = #expression'
exec sp_executesql #SQLString, N'#expression nvarchar(100)',
#expression,
#result = #result output
select #ResultVar = #result
if( #ResultVar <> ROUND( #ResultVar, 2 ,1))
set #ResultVar = cast( ROUND( #ResultVar, 2 ,1) + .01 as numeric(18,2))
RETURN #ResultVar
END
When i try to execute it
select dbo.CalculateElementFunc()
i get this error
Msg 557, Level 16, State 2, Line 1
Only functions and some extended stored procedures can be executed from within a function.
Please Advice
What you want to do is not recommended in SQL Server. First, it is really hard. As you have learned, a SQL Server function cannot execute dynamic SQL.
This is subtly in the documentation:
EXECUTE statements calling extended stored procedures.
exec and sp_executesql are not extended stored procedures.
What can you do? Here are some options:
Is a stored procedure instead of a UDF a possibility? Stored procedures can execute the dynamic SQL.
Can you get around the problem of expression evaluation? Perhaps dynamic SQL can be used one level up in your code.
You can execute an extended stored procedure that starts another transaction and executes the dynamic SQL. Think: really bad performance.
You can write a CLR extended function.
Limitations on SQL User Defined Functions:
Non-deterministic build in functions cannot be used in user defined functions. e.g. GETDATE() or RAND().
XML data type is not supported.
Dynamic SQL queries are not allowed.
User defined functions does not support any DML statements (INSERT, UPDATE, DELETE) unless it is performed on Table Variable.
We cannot make a call to the stored procedure. Only extended stored procedure can be called from function.
We cannot create Temporary tables inside UDFs.
It does not support Error Handling inside UDF. Although, we can handle errors (RAISEERROR, TRY-CATCH) for the statements which uses this function.
And it looks like you are using/calling a stored procedure inside your User Defined Function. It is not the expression that's bugging you, it's that stored procedure call.
Try to replace it with some logic to achieve your desired output.
Hope this is helpful. If it helps to solve your problem then don't forget to mark it as an answer.
I'm currently trying to create a function in a database that was created in a stored procedure.
Set #sql = 'USE [' + #dbname + '] GO CREATE FUNCTION dbo.functionname(#trajectid int)
RETURNS int
AS
BEGIN
DECLARE #result int;
(SELECT #result = SUM(duur) FROM AgendaItems WHERE trajectid = #trajectid)
RETURN #result
END'
exec(#sql)
What we want to achieve is using the function in the table definitions (also in stored procedures)
gebruikt AS [dbo].functionname([id]),
We tried using Maindatabase.dbo.functionname, which returned an error:
A user-defined function name cannot be prefixed with a database name in this context.
Thanks in advance for any help.
Sorry for being straight but, you simply should not use a stored procedure to create DDL - and in fact, the system is preventing you from doing that, as it's really a bad practice.
There are workarounds, but you should really change the way you are handling the process that you want to create - that would be the only real solution
We have a process that updates certain tables based on a parameter passed in, specifically a certain state. I know organizationally this problem would be eliminated by using a single table for this data, but that is not an option -- this isn't my database.
To update these tables, we run a stored procedure. The only issue is that there was a stored procedure for each state, and this made code updates horrible. In order to minimize the amount of code needing to be maintained, we wanted to move towards a single stored procedure that takes in a state parameter, and updates the correct tables. We wanted this without 50 If statements, so the only way I could think to do this was to save the SQL code as text, and then execute the string. IE:
SET #SSQL = 'UPDATE TBL_' + #STATE +' SET BLAH = FOO'
EXEC #SSQL;
I was wondering if there was a way to do this without using strings to update the correct tables based on that parameter. These stored procedures are thousands of lines long.
Thanks all!
Instead save entire script as SQL text and execute it, just update the required table using like code below as where you need and rest continue as it is
EXEC('UPDATE TBL_' + #STATE +' SET BLAH = FOO')
You could, indeed, use dynamic SQL (the exec function) - but with long, complex stored procedures, that can indeed be horrible.
When faced with a similar problem many years ago, we created the stored procedures by running a sort of "mail-merge". We'd write the procedure to work against a single table, then replace the table names with variables and used a PHP script to output a stored procedure for each table by storing the table names in a CSV file.
You could replicate that in any scripting language of your choice - it took about a day to get this to work. It had the added benefit of allowing us to easily store the stored proc templates in source code control.
You can safely use sp_executesql which is fairly more appropriate than a simple EXEC command. To do so, even with input and output parameters :
DECLARE #sql nvarchar(4000),
#tablename nvarchar(4000) = 'YOUR_TABLE_NAME',
#params nvarchar(4000),
#count int
SELECT #sql =
N' UPDATE ' + #tablename +
N' SET Bar = #Foo;' +
N' SELECT #count = ##rowcount'
SELECT #params =
N'#Foo int, ' +
N'#count int OUTPUT'
EXEC sp_executesql #sql, #params, 2, #count OUTPUT
SELECT #count [Row(s) updated]
I encourage you reading the related part of the article mentionned here.
In SQL Server 2005, is there a concept of a one-time-use, or local function declared inside of a SQL script or Stored Procedure? I'd like to abstract away some complexity in a script I'm writing, but it would require being able to declare a function.
Just curious.
You can create temp stored procedures like:
create procedure #mytemp as
begin
select getdate() into #mytemptable;
end
in an SQL script, but not functions. You could have the proc store it's result in a temp table though, then use that information later in the script ..
You can call CREATE Function near the beginning of your script and DROP Function near the end.
Common Table Expressions let you define what are essentially views that last only within the scope of your select, insert, update and delete statements. Depending on what you need to do they can be terribly useful.
I know I might get criticized for suggesting dynamic SQL, but sometimes it's a good solution. Just make sure you understand the security implications before you consider this.
DECLARE #add_a_b_func nvarchar(4000) = N'SELECT #c = #a + #b;';
DECLARE #add_a_b_parm nvarchar(500) = N'#a int, #b int, #c int OUTPUT';
DECLARE #result int;
EXEC sp_executesql #add_a_b_func, #add_a_b_parm, 2, 3, #c = #result OUTPUT;
PRINT CONVERT(varchar, #result); -- prints '5'
The below is what I have used i the past to accomplish the need for a Scalar UDF in MS SQL:
IF OBJECT_ID('tempdb..##fn_Divide') IS NOT NULL DROP PROCEDURE ##fn_Divide
GO
CREATE PROCEDURE ##fn_Divide (#Numerator Real, #Denominator Real) AS
BEGIN
SELECT Division =
CASE WHEN #Denominator != 0 AND #Denominator is NOT NULL AND #Numerator != 0 AND #Numerator is NOT NULL THEN
#Numerator / #Denominator
ELSE
0
END
RETURN
END
GO
Exec ##fn_Divide 6,4
This approach which uses a global variable for the PROCEDURE allows you to make use of the function not only in your scripts, but also in your Dynamic SQL needs.
In scripts you have more options and a better shot at rational decomposition. Look into SQLCMD mode (SSMS -> Query Menu -> SQLCMD mode), specifically the :setvar and :r commands.
Within a stored procedure your options are very limited. You can't create define a function directly with the body of a procedure. The best you can do is something like this, with dynamic SQL:
create proc DoStuff
as begin
declare #sql nvarchar(max)
/*
define function here, within a string
note the underscore prefix, a good convention for user-defined temporary objects
*/
set #sql = '
create function dbo._object_name_twopart (#object_id int)
returns nvarchar(517) as
begin
return
quotename(object_schema_name(#object_id))+N''.''+
quotename(object_name(#object_id))
end
'
/*
create the function by executing the string, with a conditional object drop upfront
*/
if object_id('dbo._object_name_twopart') is not null drop function _object_name_twopart
exec (#sql)
/*
use the function in a query
*/
select object_id, dbo._object_name_twopart(object_id)
from sys.objects
where type = 'U'
/*
clean up
*/
drop function _object_name_twopart
end
go
This approximates a global temporary function, if such a thing existed. It's still visible to other users. You could append the ##SPID of your connection to uniqueify the name, but that would then require the rest of the procedure to use dynamic SQL too.
Just another idea for anyone that's looking this up now. You could always create a permanent function in tempdb. That function would not be prefixed with ## or # to indicate it's a temporary object. It would persist "permanently" until it's dropped or the server is restarted and tempdb is rebuilt without it. The key is that it would eventually disappear once the server is restarted if your own garbage collection fails.
The scope of the function would be within TempDB but it could reference another database on the server with 3 part names. (dbname.schema.objectname) or better yet you can pass in all the parameters that the function needs to do its work so it doesn't need to look at other objects in other databases.
How can I ignore an output parameter of a stored procedure? I'm calling the procedure from another procedure, e.g.:
DECLARE #param1 integer
EXEC mystoredprocedure
#in_param_1,
#in_param2_,
#param1 OUTPUT,
-- what do I type here to ignore the second output param??
I'm using T-SQL (MS SQL 2005).
You can just use NULL as the last parameter, and it should work fine - as long as that parameter isn't expected for input logic in the proc as well.
In your case, you'd call the proc as
exec mystoredproc #in_param_1, #in_param2_, #param1 OUTPUT, null
Here's another example that for the same scenario...
create proc MyTempProc
(#one int,
#two int out,
#three int out)
AS
begin
set #two = 2
set #three = 3
select #one as One
end
go
declare #p1 int,
#p2 int
set #p1 = 1
exec MyTempProc #p1, #p2 out, null
print #p1
print #p2
The output parameter has to have a default in order for you to not pass it. See below
create proc x (#in int = null, #out int = null OUTPUT)
as
Begin
Select #in
End
exec x 1
EDIT
To clarify a little, the error is being returned because the stored procedure that is being called has a parameter defined (in this case #param1), for which no default is defined. (i.e. #param1 int rather than #param int = -1) In the case where neither a parameter or default is specified for a defined parameter of a stored procedure when calling the stored procedure an error will occur. The same thing would happen if you tired to omit an input parameter that does not have a default specified.
You'll probably have to just ignore the OUTPUT param yourself by just not doing anything with the value. It's not like the overhead of that variable is an issue or anything. The only issue here is that your code will be a little bit uglier. So slap a comment on there explaining that the OUTPUT param isn't being used and everything should be alright.
If the SP you're calling expects a parameter to be passed, you have to have one there. Even if you disregard the output of it, it is part of the structure of the SP.
Think of parameters as a "Data Contract". If they're not defaulted, they're required. Even if the values are disregarded.
Forcing you to declare a dummy value you'll never read is the cost of calling that stored proc that may be used by something that DOES need to utilize the value of the output parameter.