I would like to update data in a table (for sqlserver and oralce version).
I created a stored procedure as below, but i would like to convert it to SQL function, is it possible to update data within SQL function please?
CREATE PROCEDURE updatetable (#A1 INTEGER, #A2 VARCHAR(4000) )
AS
BEGIN
BEGIN
UPDATE table SET column1= column1+ #A1 WHERE column2= #A2 ;
END
END
For SQL Server.
Simply put:
No.
A function can't change the system (really gross and dangerous hacks aside). From the documentation (emphasis mine):
Specifies that a series of Transact-SQL statements, which together do not produce a side effect such as modifying a table
If you can explain what you're trying to accomplish with a function that you can't accomplish with a stored procedure, you might be asking a question that has more than a yes/no answer, and you might get useful alternatives.
Related
Procedure FunctionX, Line 345
Invalid use of a side-effecting operator 'EXECUTE STRING' within a
function.
I get the above error when I execute a dynamic statement inside a function in SQL Server 2012.
Is there a workaround for this? Any tricks?
PS: The sproc (stored procedure) is much too lengthy for its body to be inserted as-is inside the function.
DECLARE #execsql NVARCHAR(2000)
Set #execsql = 'INSERT INTO #TABLE1 EXEC SPROC1 ' + #ID_COMPANY + ',' + #ID_COUNTRY
exec (#execsql)
Many thanks in advance.
Also, I need to be able to delete inside the function as well. I know this contradicts the definition of functions but I am wondering if there are some tricks that can be used
No there are no tricks, see The Curse and Blessings of Dynamic SQL
Dynamic SQL in User-Defined Functions
This is very simple: you cannot use dynamic SQL from used-defined
functions written in T-SQL. This is because you are not permitted to do
anything in a UDF that could change the database state (as the UDF may
be invoked as part of a query). Since you can do anything from dynamic
SQL, including updates, it is obvious why dynamic SQL is not
permitted.
I've seen more than one post on the newsgroups where people have been
banging their head against this. But if you want to use dynamic SQL in
a UDF, back out and redo your design. You have hit a roadblock, and in
SQL 2000 there is no way out.
In SQL 2005 and later, you could implement your function as a CLR
function. Recall that all data access from the CLR is dynamic SQL.
(You are safe-guarded, so that if you perform an update operation from
your function, you will get caught.) A word of warning though: data
access from scalar UDFs can often give performance problems. If you
say
SELECT ... FROM tbl WHERE dbo.MyUdf(somecol) = #value
and MyUdf performs data access, you have more or less created a hidden
cursor.
I was having this same problem with dynamic OPENQUERY statements inside a multi-line table-valued function. SQL Server is trying to prevent users with only db_datareader access, who can select from these functions, from performing SQL injections. Long story short, remove as many single quotes as you can and find a way to do the same thing without using EXEC.
Instead of doing this:
Set #execsql = 'INSERT INTO #TABLE1 EXEC SPROC1 ' + #ID_COMPANY + ',' + #ID_COUNTRY
Do something like this:
INSERT INTO #TABLE1
SELECT *
FROM --some unfiltered version of the table your stored procedure uses
WHERE company = #ID_COMPANY
AND country = #ID_COUNTRY
Since you're calling a function from a stored procedure you can already be sure the table will be up to date. In my case, I was able to have a job refresh the function's underlying table using the stored procedure once every morning. You could also use a trigger to do that.
I would like to call a stored procedure or user-defined function that returns a dynamic table that is created via a pivot expression. I don't know the number of columns up front.
Is this possible? (I am not interested in temporary tables)
You can do that via stored procedure as it can return any kind of table, question is what are you trying to achieve and what will you do with data that you have no idea about?
This cannot be done with functions (as the returned table structure must be pre-defined), but it can be done with a stored proceed. Some psuedo-code:
CREATE PROCEDURE Foo
As
DECLARE #Command
SET #Command = 'SELECT * from MyTable'
-- For debugging, work in an optional PRINT #Command statement
EXECUTE (#Command)
RETURN 0
When you run stored procedure Foo, it builds your query as a string in #Command, and then dynamically executes it without knowing anything about what is being queried or returned, and the data set returned by that EXECUTE statement is "passed back" to the process that called the procedures.
Build your query with care, this stuff can be really hard to debug. Depending on your implementation, it might be a source of SQL injection attacks (remember, the stored procedure really doesn't know what that dynamic query is going to do). For quick stuff, EXECUTE() works fine, but for safer and more useful (if elaborate) solutions, look into sp_ExecuteSQL.
Yes, you can do this from a Stored Procedure, but not from a user-defined Function. It is worth looking into the Table Value Function, I believe you can also return a dynamic table from there, but I have not used that myself.
If I want to execute stored procedure using the values returned from result set of a select statement. So number of times SP should get executed is equal to the number of result set from the select statement.
Is there any other way than using a cursor to do the above?
UPDATE
Can anyone please give sample code with While loop at least?
In T-SQL there are only 2 ways for iteration. While loop or cursors. If you don't want to use cursors, you had to use while loop as James Wiseman said.
ANother way to accomplish this situation is SQL CLR. If you are using SQL CLR, you can use all C# (or VB.Net) iterations to reach your goal.
I would convert the proc to use a table variable and pass the data set in using that. The beauty of this is that once you have made the change, you can use the same proc for either single row inserts or mulitple and do it in sets not row-by-row.
You need SQL Server 2008 for this one.
You'll have to convert your proc to a Multi-Statement Tabled Value UDF..
create function dbo.udf_Whatever_That_Proc_Did(
#SameOldParam as int
)
AS Begin
Declare --same variables here
/*same code in your proc that does not
- invoke nondeterministic built-in function
- change state of database
- return messages to caller
*/
Return
End
To utilize function:
Select *
from dbo.udf_Whatever_That_Proc_Did(9999)
An alternative to a cursor is a while loop, which is sometimes recommended as an alternative to SQL Cursors.
Is the problem that you want to avoid using the cursor, or is it that you are wating to avoid iteration altogether?
Maybe this could help you, create a UDF and then call the stored proc from within that UDF. Since you can call UDF in a select query, it should execute the stored proc as many times as you have results in select query.
Is there a way via metadata (Information_Schema, perhaps?) to get a list of the columns a sproc will return? I'm trying to automate some code generation and that would help tremendously...
Unless you're prepared to parse the contents of ROUTINE_DEFINITION in INFORMATION_SCHEMA.ROUTINES, then your best bet will be to execute the procedures, and read the column information from the records returned.
In .NET you can do this by reading the results of the stored procedure into a DataTable and querying the Columns property.
The reason there's no easy way to do this is a stored procedure could potentially return different result sets based on the parameters. There's no fixed result set format like there is with user defined functions.
Edit
As mentioned in the other answer, you will need to use SET FMTONLY ON to ensure no data is returned. There are some situations where SET FMTONLY won't work, e.g. when using #temp tables in your stored procedures, but there is a workaround.
I just ran Profiler to see how Visual Studio does this for the strongly typed dataset drag and drop.
This is the code it sent.
SET NO_BROWSETABLE ON;
SET FMTONLY ON;
exec dbo.aspnet_Roles_GetAllRoles #ApplicationName=NULL
So I presume there might not be any "more official" way of doing it.
Obviously you would need to bear in mind that a single stored procedure might return multiple result sets or different result sets dependant on the parameters passed.
For people on 2012+ another approach might be to use sp_describe_first_result_set
My way of doing this:
Edit the stored procedure to have an INTO clause:
Change
Select * from tablename
to
Select * INTO _tablename FROM tablename
This creates a table in the database.
Then, use SELECT * FROM INFORMATION_SCHEMA WHERE TABLE_NAME = '_tablename'
Don't forget to undo the modification to the sproc.
I have a Stored Procedure that rolls-back a series of operations. I want to call this from within another SP.
The problem is that the inner SP returns a record set with a single value that indicates the degree of success.
This approach worked well and has some advantages in our context, but in retrospect, I would have done it the conventional way with a Return value or an Output parameter.
I could always change this SP to use this approach and modify the calling code, but a) I don't want to dabble with any more code than I have to, and b) at an intellectual level, I'm curious to see what alternative solution there may be, if any.
How (if at all) can I call this SP and determine the value of the singleton recordset returned?
Thanks
A stored procedure returns a record set like any other, so you can actually do this:
INSERT INTO MyTable (
MyValue
)
EXEC dbo.MyStoredProcedure
The EXEC takes the place of a SELECT statement. To get the value, just SELECT from the table you inserted into. Typically, this would be a temp table.
The other option is to convert the stored procedure that returns a recordset into a function that returns a table.
Ant's approach is probably best if you want to minimize the changes to your system.
Normally you would use a temporary table for that approach since you can't use an exec statement to insert into a table variable.
Here's a variation which will work well if you need to use this for MULTIPLE recordsets.
CREATE TABLE #outsidetable (...)
exec spInsideProcedure
SELECT * FROM #outsidetable
inside spInsideProcedure
INSERT INTO #outsidetable SELECT <blah blah blah>
I tried Ant's approach and it worked a treat:
Declare #Success tinyint
Declare #Response Table (Success int)
Insert into #Response(Success)
Exec Fix_RollbackReturn 12345, 15
Select #Success=Success from #Response
As you can see I used a Table Variable rather than a temporary table because slightly more efficient than a temporary table.
Thanks for all your help guys.
EDIT: It appears that Dave was right after all. That is, my Exec-into-Table-variable approach worked on my SQL2005 development machine, but when moved to the Live (SQL2000) machine it objected, so I had to change to the temporary table approach.
It's a little annoying, especially since in a couple of weeks we are upgrading to SQL2005 across the board(!).