I need to run a test on stored procedure in a client's database. Is there anyway to test the stored procedure without affecting the data in the database?
For example, there is an insert query in the SP, which will change the data of the database.
Is there anyway to solve this problem?
You could run the stored procedure in a transaction. Use this script by placing your statements between the comment lines. Run the whole script, your transaction will be in an uncommitted state. Then, highlight the line ROLLBACK or COMMIT and execute either accordingly to finish.
Always have backups.
If possible work in a sandbox away from your clients data as a matter of principle.
Be aware that you could be locking data which could be holding up other sql statements by your client while you are deciding whether to commit or rollback.
BEGIN TRANSACTION MyTransaction
GO
-- INSERT SQL BELOW
-- INSERT SQL ABOVE
GO
IF ##ERROR != 0
BEGIN
PRINT '--------- ERROR - ROLLED BACK ---------'
ROLLBACK TRANSACTION MyTransaction
END
ELSE
BEGIN
PRINT '--------- SCRIPT EXECUTE VALID ---------'
PRINT '--------- COMPLETE WITH ROLLBACK OR COMMIT NOW! ---------'
--ROLLBACK TRANSACTION MyTransaction
--COMMIT TRANSACTION MyTransaction
END
If the SP is meant to change data, and if you don't permit the data to change, then how will you "test" the SP? Will you just make sure it doesn't die? What if it returns no errors, but inserts no data?
You can follow a similar path to what Valamas suggested, but you will also need to actually test the SP. For instance, if particular data are meant to be inserted based on particular parameter values, then you'll have to:
Start a transaction
Create any test data in the database
Call the SP with the particular parameter values
Still within the transaction, check the database to see if the correct rows were inserted
Roll back the transaction
I can't show you the code, but I have had success in doing the above in code in .NET, using the Visual Studio unit test framework. One could do the same with NUnit or any other unit test framework. I did not use the Database Unit Test feature of Visual Studio Database Projects. I simply did the steps above in code, using ADO.NET and the SqlTransaction class to control the transaction.
Related
edited terminology for accuracy:
We have large, daily flows of data within our data-mart. Some of the largest, done with Stored procedures managed by SSIS, take several hours. These long-running stored procedures are preventing the transaction-log from clearing (which compounds the issue because we have numerous SP's running at once, which are then all writing to the T-log with no truncate). Eventually this breaks our database and we're forced to recover from the morning snapshot.
We have explored doing "sub"-commits within the SP, but as I understand it you can't fully release the transaction log within an active stored procedure, because it is itself a transaction.
Without refactoring our large SP's to run in batches, or something to that effect, is it possible to commit to the transaction log periodically within an active SP, so that we release the lock on the transaction log?
edit / extension:
Perhaps I was wrong above:
Will committing intermittently within the SP allow the transaction-log to truncate?
Will committing intermittently within the SP allow the transaction-log to truncate?
If the client starts a transaction, it's not recommended to COMMIT that transaction inside a stored procedure. It's not allowed to exit the stored procedure with a different ##trancount than it was entered with.
The following pattern is technically allowed, although I have never seen it used in the real world:
use tempdb
if ##trancount > 0 rollback
go
drop table if exists T
create table T(id int identity)
go
create or alter procedure tranTest
as
begin
insert into T default values
commit transaction
begin transaction
end
go
begin transaction
exec tranTest
select * from T
rollback
go 5
It would be deeply confusing for client code to rollback a transaction and not have the stored procedure's work rolled back.
If the client doesn't start a transaction, you can have multiple transactions inside a stored procedure, but the smallest granularity for a transaction is a single DML statement. So each INSERT, UPDATE, DELETE, or MERGE would be run in a single transaction.
The practical solutions to this are, in descending order of goodness:
1) Increase the storage available to the log file to accommodate the transactions.
2) Refactor the ETL to use shorter transactions, possibly readying data in stating tables and loading or switching it in in a single, final transaction
3) Refactor the ETL to run in smaller batches.
I have several stored procedures in a job, and In one of them I Begin a transaction to delete some rows and if rows are greater than 10 then I Roll back. however if there are not I don't want to commit straight away, because 2 stored procedure later I do something similar. however if count is greater than 10 in this instance I want it rolled back all the way to when I stared the transaction (two stored procedures ago)
Is it possible to start a transaction in a store procedure and have multiple roll backs and Commit right at the end somewhere or do I have to put all the code into 1 store procedure to do that?
This sounds incredibly prone to failure.
Regardless, you will need to start the transaction in your code then, while using the same connection, execute the procs. The code would then commit or rollback once all the procs have executed.
Assuming this is c#, see the following question for answers: Call multiple SQL Server stored procedures in a transaction
You can write several stored procedures and then execute them as nested.
You can declare variables in order to get the result and use if statement to commit or rais error for catch block or rollback transaction
Question:
Normally, you can undo a sql command with rollback.
BEGIN TRY
BEGIN TRANSACTION
/* run all your SQL statements */
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
My question now:
If 'one' did this
UPDATE TABLE_X SET FIELD_X = 'bla'
GO
in SSMS (notice the go at the end) and forgot to specify the WHERE clause,
is it possible (and how) to rollback the implicit transaction that SSMS executed this command in (statements with go at the end are implicitly executed in a transaction) ?
Note:
I didn't do that, but a colleague of mine did a few days ago (without go).
I undid the damage he did (fortunately I made a backup 0.5 hours before he did that), but for the future, it would be good to know this, because this happened to me once, too.
No, you can't, not easily. Restoring from backup is the best option.
see the link below, I think it will help you
How to recover the old data from table
thanks
Arun
GO does not specify the end of an implicit transaction, but the end of a batch. That's why you won't be able (unfortunately) to ROLLBACK your UPDATE after a GO.
From the MSDN page on GO:
GO is not a Transact-SQL statement; it is a command recognized by the
sqlcmd and osql utilities and SQL Server Management Studio Code
editor.
SQL Server utilities interpret GO as a signal that they should send
the current batch of Transact-SQL statements to an instance of SQL
Server. The current batch of statements is composed of all statements
entered since the last GO, or since the start of the ad hoc session or
script if this is the first GO.
The UPDATE command will only be seen as the start of an implicit transaction if you have specified SET IMPLICIT_TRANSACTIONS ON; (see here). In that case, a number of commands (CREATE, DELETE, UPDATE etcetera) will automatically start a new implicit transaction, and that transaction will not end until you issue a ROLLBACK or a COMMIT.
(See for more info on the difference between transactions and batches in SQL Server for example this question on ServerFault: SQL Server: Statements vs. Batches vs. Transactions vs. Connections.)
I have a (SQL 2005) stored procedure that processes a giant table and groups old data from the past (up til one year ago). It has this main steps:
copy old data grouped to a new table
copy recent data as is to the new table
rename table
Now I want to log every run and every step in logging tables. However I start a transaction in the beginning so that I can rollback the whole batch if something goes wrong. But that would also rollback my logging which isn't what I want.
How can I resolve this?
Log to a table variable as this doesn't get rolled back with the transaction then at the end of the procedure after commit or rollback insert the contents of the table variable into your permanent logging table.
Use SET XACT_ABORT ON to force rollback
To catch all errors (where code runs), use TRY/CATCH blocks.
Then, you can simply log the errors in your CATCH blocks.
Example here (can add your own logging): Nested stored procedures containing TRY CATCH ROLLBACK pattern?
Personally, I find this more elegant than using table variables.
Say I have a stored procedure consisting of several separate SELECT, INSERT, UPDATE and DELETE statements. There is no explicit BEGIN TRANS / COMMIT TRANS / ROLLBACK TRANS logic.
How will SQL Server handle this stored procedure transaction-wise? Will there be an implicit connection for each statement? Or will there be one transaction for the stored procedure?
Also, how could I have found this out on my own using T-SQL and / or SQL Server Management Studio?
Thanks!
There will only be one connection, it is what is used to run the procedure, no matter how many SQL commands within the stored procedure.
since you have no explicit BEGIN TRANSACTION in the stored procedure, each statement will run on its own with no ability to rollback any changes if there is any error.
However, if you before you call the stored procedure you issue a BEGIN TRANSACTION, then all statements are grouped within a transaction and can either be COMMITted or ROLLBACKed following stored procedure execution.
From within the stored procedure, you can determine if you are running within a transaction by checking the value of the system variable ##TRANCOUNT (Transact-SQL). A zero means there is no transaction, anything else shows how many nested level of transactions you are in. Depending on your sql server version you could use XACT_STATE (Transact-SQL) too.
If you do the following:
BEGIN TRANSACTION
EXEC my_stored_procedure_with_5_statements_inside #Parma1
COMMIT
everything within the procedure is covered by the transaction, all 6 statements (the EXEC is a statement covered by the transaction, 1+5=6). If you do this:
BEGIN TRANSACTION
EXEC my_stored_procedure_with_5_statements_inside #Parma1
EXEC my_stored_procedure_with_5_statements_inside #Parma1
COMMIT
everything within the two procedure calls are covered by the transaction, all 12 statements (the 2 EXECs are both statement covered by the transaction, 1+5+1+5=12).
You can find out on your own by creating a small stored procedure that does something simple, say insert a record into a test table. Then Begin Tran; run sp_test; rollback; Is the new record there? If so, then the SP ignores the outside transaction. If not, then the SP is just another statement executed inside the transaction (which I am pretty sure is the case).
You must understand that a transaction is a state of the session. The session can be in an explicit transaction state because there is at least one BEGIN TRANSACTION that have been executed in the session wherever the command "BEGIN TRANSACTION" has been throwed (before entering in a routine or inside the routine code). Otherwise, the state of the session is in an implicit transaction state. You can have multiple BEGIN TRANSACTION, but only the first one change the behavior of the session... The others only increase the ##TRANCOUNT global sesion variable.
Implicit transaction state means that all SQL orders (DDL, DML and DCL comands) wil have an invisble integrated transaction scope.