Main Stored Procedure to Execute another Proc depending on Input and output in a CSV - sql

I have two procedures which basically give an Table output. I have the third Procedure which basically calls these two procedures and gives the output in an csv file format.
Can anyone help me in building this the right way. Below is something I am trying to do:
Each of the two Procedures gives out an output with like around 100k rows, I want to capture that and want to give the output here from the Main procedure in a csv file.(Please let me know if you need more info)
Create PROC MAIN
#InputParam int
AS
Begin
Set NOCOUNT ON;
BEGIN TRY
IF #InputParam not in (1,2)
BEGIN
Print 'Error Message'
END
Else
begin
IF #InputParam=1
BEGIN
Exec StoredProc1
Print 'Stored Procedure StoredProc1 ended at '+Convert(Varchar(25),GETDATE(),21);
End
Else
Begin
Print 'StoredProc1 does not exist'
END
IF #InputParam=2
BEGIN
Exec StoredProc2
Print 'Stored Procedure StoredProc2 ended at '+Convert(Varchar(25),GETDATE(),21);
End
Else
BEGIN
Print 'StoredProc2 does not exist'
END
END -- This is END for ELSE loop
END TRY
Begin Catch
Print 'Input Validation Catch Block with # '+ Convert(Varchar(5),ERROR_NUMBER())+' Msg: '+ERROR_MESSAGE();
End Catch
END

What you may want to try is to declare a table vairable in your main stored procedure. This table needs to match the output from the sub procedures exactly. The syntax is:
DECLARE #Temp TABLE(
Filed1 INT,
Field2 VARCHAR(100),
etc
)
Then you can execute your sub stored procedure and insert into the table you defined, like this:
INSERT INTO #Temp
EXEC StoredProc1
And finally, select from the #Temp
SELECT * FROM #Temp
You will obviously need to fit your exact requirements into this, but it should assist I hope
You cannot output to a CSV directly from a Stored Procedure without some form of code, so that really depends what you are developing with (ie what will call the stored procedure). You could perhaps build a SQL Job to output to CSV.

Related

Error Trap SQL Statement Linked Server

I am using SQL2008R2 and have a large stored procedure containing a section of SQL that INSERTS a record to a LINKED server (also SQL2008R2). I want to be able to continue on with the stored procedure if the linked server happens to be down, unavailable, etc.
What would be the best way to to do this? Could an example be provided?
IF #InsertToLinkServer = 1
BEGIN
INSERT INTO [Server].[Database].[dbo].[Table]
(field1) VALUES (value1)
END
ContinueHereifFailed:
Other sql statements....
Try to use dynamic sql wrapped in the TRY/CATCH block
DECLARE #dsql nvarchar(max)
BEGIN TRY
SET #dsql = 'INSERT [Server].[Database].[dbo].[Table](field1) VALUES (value1)'
EXEC sp_executesql #dsql
END TRY
BEGIN CATCH
PRINT 'Error'
END CATCH

What leads to this strange SQL behavior?

Running SQL 2005 X64.
First, create the following stored proc on a database:
CREATE PROCEDURE dbo.Test
#Value int = null
AS
BEGIN
IF (IsNull(#Value, '') = '')
SELECT '*I am NULL!*'
ELSE
SELECT 'I am ' + CONVERT(varchar(20), #Value)
END
Try executing the above proc as follows, and you get the result below:
EXEC dbo.Test
I am NULL!
Now, ALTER the proc so that the EXEC statement is part of the sproc itself:
ALTER PROCEDURE dbo.Test
#Value int = null
AS
BEGIN
IF (IsNull(#Value, '') = '')
SELECT 'I am NULL!'
ELSE
SELECT 'I am ' + CONVERT(varchar(20), #Value)
END
EXEC dbo.Test
If you execute it now, you get...
I am NULL!
I am NULL!
I am NULL!
...ad infinitum until the output breaks with this error:
Msg 217, Level 16, State 1, Procedure
Test, Line 16 Maximum stored
procedure, function, trigger, or view
nesting level exceeded (limit 32).
Ignoring for the moment that this isn't at all a standard practice and that most likely someone would do something like this only by accident, could someone please provide some low-level insight on what SQL 2005 is "thinking" when the second incarnation of this proc is executed?
Your code is behaving as expected. The procedure is calling itself recursively.
If you do not want that, try this:
ALTER PROCEDURE dbo.Test
#Value int = null
AS
BEGIN
IF (IsNull(#Value, '') = '')
SELECT 'I am NULL!'
ELSE
SELECT 'I am ' + CONVERT(varchar(20), #Value)
END
GO
EXEC dbo.Test
If you do want to use recursion, you have to define a base case (AKA "exit condition") which will make stored procedure exit the recursion stack.
The recursion is because everything is being considered part of the proc, not just the BEGIN to END block.
From my comment:
No great mystery. It's going to treat everything until the next GO or other indicator of the end of the batch as part of the proc. The outermost BEGIN and END are not required syntax as part of the procedure.
It's called recursion, as others have mentioned.
You can avoid it as #Adrian has shown (using 'GO' to prevent the sp from calling itself), or you can also escape it using a control structure...
Here's a sample / experiment you can study if you want to learn about recursion: http://msdn.microsoft.com/en-us/library/aa175801.aspx
It allows for 32 nested calls. and with every Exec call you are nesting it forever. So think recursively.
Exec proc
Select
Exec
Select
exec
Infinitely.
once it reaches the 32nd nested calls it hits its maximum and says whoa i can not continue.
My reading of the question is not "Why is my SP exhibiting recursion?" but "Why is recursion limited to 32 and how do i get around that?"
I had completely forgotten that SQL Recursion dies on you like that.
An answer I just worked out is to make use of TRY-CATCH and ##NestLevel. The below is a small demonstrator rig. In your code it would be far better to have an independent end condition, for example running out of chunks to process.
My code has been mangled by the editor, I have no time to work round your issues.
BEGIN TRY DROP PROCEDURE dbo.Nester END TRY BEGIN CATCH END catch
GO
CREATE PROCEDURE dbo.Nester #NestLevel INT = 0 OUT
AS
BEGIN
DECLARE #MaxActNestLevel INT = 40;
SELECT #NestLevel += 1;
PRINT (CONVERT(sysname, ##NestLevel) + ' ' + CONVERT(sysname, #NestLevel))
IF #NestLevel < #MaxActNestLevel
BEGIN TRY
EXEC dbo.Nester #NestLevel OUT
END TRY
BEGIN CATCH
PRINT 'Catch Block'
PRINT (ERROR_NUMBER())
SELECT #NestLevel += 1;
IF ##NestLevel < 30 --AND ERROR_NUMBER() = 217
BEGIN
EXEC dbo.Nester #NestLevel OUT
END
ELSE
THROW
END CATCH
END
GO
EXEC dbo.Nester;

Can you detect INSERT-EXEC scenario's?

Is it possible to DETECT whether the current stored procedure is being called by an INSERT-EXEC statement?
Yes, I understand we may want to no longer use INSERT-EXEC statements...that is NOT the question I am asking.
The REASON I am using INSERT-EXEC is because i am hoping to promote re-use of stored procedures rather than re-writing the same SQL all the time.
Here's why I care:
Under the INSERT-EXEC scenario the original error message will get lost once a ROLLBACK is requested. As such, any records created will now be orphaned.
Example:
ALTER PROCEDURE [dbo].[spa_DoSomething]
(
#SomeKey INT,
#CreatedBy NVARCHAR(50)
)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION
-- SQL runs and throws an error of some kind.
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRAN
-- If this procedure is called using an INSERT-EXEC
-- then the original error will be lost at this point because
-- "Cannot use the ROLLBACK statement within an INSERT-EXEC statement."
-- will come-up instead of the original error.
SET #ErrorMessage = ERROR_MESSAGE();
SET #ErrorSeverity = ERROR_SEVERITY();
SET #ErrorState = ERROR_STATE();
RAISERROR(#ErrorMessage, #ErrorSeverity, #ErrorState)
END CATCH
RETURN ##Error
END
I've come up with a bit of a kludge (ok, it's a big kludge), based on the fact that you can't have nested INSERT--EXECUTE... statements. Basically, if your "problem" procedure is the target of an INSERT--EXECUTE, and itself contains an INSERT--EXECUTE, then an error will be raised. To make this work, you'd have to have a (quite probably pointless) INSERT--EXECUTE call in the procedure, and wrap it in a TRY--CATCH block with appropriate handling. Awkward and obtuse, but if nothing else comes up it might be worth a try.
Use the following to test it out. This will create three procedures:
IF objectproperty(object_id('dbo.Foo1'), 'isProcedure') = 1
DROP PROCEDURE dbo.Foo1
IF objectproperty(object_id('dbo.Foo2'), 'isProcedure') = 1
DROP PROCEDURE dbo.Foo2
IF objectproperty(object_id('dbo.Foo3'), 'isProcedure') = 1
DROP PROCEDURE dbo.Foo3
GO
-- Returns a simple data set
CREATE PROCEDURE Foo1
AS
SET NOCOUNT on
SELECT name
from sys.databases
GO
-- Calls Foo1, loads data into a local temp table, then returns those contents
CREATE PROCEDURE Foo2
AS
SET NOCOUNT on
CREATE TABLE #Temp (DBName sysname not null)
BEGIN TRY
INSERT #Temp (DBName)
EXECUTE Foo1
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 8164
PRINT 'Nested INSERT EXECUTE'
ELSE
PRINT 'Unanticipated err: ' + cast(ERROR_NUMBER() as varchar(10))
END CATCH
SELECT *
from #Temp
GO
-- Calls Foo2, loads data into a local temp table, then returns those contents
CREATE PROCEDURE Foo3
AS
SET NOCOUNT on
CREATE TABLE #Temp2 (DBName sysname not null)
INSERT #Temp2 (DBName)
EXECUTE Foo2
SELECT *
from #Temp2
GO
EXECUTE Foo1 will return the "base" data set.
EXECUTE Foo2 will call Foo1, load the data into a temp table, and then return the contents of that table.
EXECUTE Foo3 attempts to do the same thing as Foo2, but it calls Foo2. This results in a nested INSERT--EXECUTE error, which is detected and handled by Foo2's TRY--CATCH.
Maybe ##NESTLEVEL can help:
http://msdn.microsoft.com/en-us/library/ms187371.aspx

SQL select print out results of stored procedure

My businesses application supports only reporting with selected data from SQL server.In one business process I have very complicated stored procedure which using others stored procs and it was designed to print out results as log of job done. What I want to catch that print out and select it as varchar(max) so my app can handle that data and display to user.
Here is sample scenario described in TSQL code:
create procedure sp_test_print_out
as
begin
Print 'Test';
print 'Test 1';
end
go
create procedure sp_test_print_out_to_select
as
declare #printOut varchar(max)
set #printOut = exec sp_test_print_out --How I can achieve this ?
select #printOut
end
go
exec sp_test_print_out_to_select
You can try setting the values in output parameter
create procedure sp_test_print_out
#printMessages varchar(max) output
as
begin
set #printMessages='Test'
Print 'Test';
set #printMessages= #printMessages + CHAR(10)
set #printMessages= #printMessages + 'Test 1'
print 'Test 1';
end
go
create procedure sp_test_print_out_to_select
as
begin
declare #printOut varchar(max)
exec sp_test_print_out #printOut output -- can be achieved using output parameter ?
select #printOut
end
go
exec sp_test_print_out_to_select
There is also one rough and probably BAD way to get selected data from print commands inside stored procedure.
Command xp_cmdshell and sqlcmd can do the JOB. Xp_cmdshell is mostly disabled and not allowed to use at most of SQL servers because of security reasons.
Here is code:
CREATE TABLE #temp
(OUTPUT VARCHAR(MAX));
declare #cmd varchar(800);
set #cmd = 'sqlcmd -d RobotTest -Q "exec sp_test_print_out"';
INSERT INTO #TEMP
exec xp_cmdshell #cmd ;
select output from #temp;
drop table #temp;

Find out the calling stored procedure in SQL Server

Is it possible to find out who called a stored procedure?
For example, say I get an error in proc3. From within that proc I want to know if it was called by proc1 or proc2.
I would use an extra input parameter, to specify the source, if this is important for your logic.
This will also make it easier to port your database to another platform, since you don't depend on some obscure platform dependent function.
There is no nice automatic way to do this (alas). So it really depends on how much you are prepared to (re)write your procs in order to be able to do this.
If you have a logging mechanism, you might be able to read the log and work out who called you.
For example, if you implement logging by inserting to a table, for example:
CREATE TABLE Log
(timestamp dattime,
spid int,
procname varchar(255),
message varchar(255) )
... text of proc ...
INSERT INTO Log
SELECT get_date(), ##spid, #currentproc, 'doing something'
-- you have to define #currentproc in each proc
-- get name of caller
SELECT #caller = procname
FROM Log
WHERE spid = ##spid
AND timestamp = (SELECT max(timestamp)
FROM Log
WHERE timestamp < get_date()
AND procname != #currentproc )
This wouldn't work for recursive calls, but perhaps someone can fix that?
Do you need to know in proc3 at runtime which caused the error, or do you just need to know while debugging?
You can use SQL Server profiler if you only need to do it during debugging/monitoring.
Otherwise in 2005 I don't believe you have the ability to stack trace.
To work around it you could add and extra parameter to proc3, #CallingProc or something like that.
OR you could add try catch blocks to proc1 and proc2.
BEGIN TRY
EXEC Proc3
END TRY
BEGIN CATCH
SELECT 'Error Caught'
SELECT
ERROR_PROCEDURE()
END CATCH
Good reference here : http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1189087,00.html
and of course always SQL Server Books Online
SQL Server 2008 does have the ability to debug through procedures however.
You could have proc1 and proc2 pass their names into proc3 as a parameter.
For example:
CREATE PROCEDURE proc3
#Caller nvarchar(128) -- Name of calling proc.
AS
BEGIN
-- Produce error message that includes caller's name.
RAISERROR ('Caller was %s.', 16,10, #Caller);
END
GO
CREATE PROCEDURE proc1
AS
BEGIN
-- Get the name of this proc.
DECLARE #ProcName nvarchar(128);
SET #ProcName = OBJECT_NAME(##PROCID);
-- Pass it to proc3.
EXEC proc3 #ProcName
END
GO
CREATE PROCEDURE proc2
AS
BEGIN
-- Get the name of this proc.
DECLARE #ProcName nvarchar(128);
SET #ProcName = OBJECT_NAME(##PROCID);
-- Pass it to proc3.
EXEC proc3 #ProcName
END
GO