Running a stored procedure using a resultset as the parameter and insert into a table - tsql

I have the question below,
I do have a query which returns 10 rows for example.
SELECT CarId FROM Car
EXEC spQ_GetCar (#CarId)
Also, I have a stored procedure that uses an id from the table above.
My question is, how can I run the stored procedure and use the output of the table as the parameter and then insert that into another temp table.
Would it be possible by using a cursor and dynamic SQL ?, has anyone faced this before ?
NOTES: I cannot create a table type to use it

A fast answer:
Create a table, then you can insert-exec:
insert yourPreparedtable
EXEC spQ_GetCar (#CarId)
The true answer:
Erland Sommarskog's article is the most in-depth resource you will find. Many alternatives with detailed advantages and disadvantages.

I achieved this by following approach below:
CREATE TABLE #TEMP (CarId INT)
DECLARE #CarId INT
DECLARE Car CURSOR
,CarId INT SET Car = CURSOR STATIC
FOR
SELECT CarId
FROM Car
OPEN Car
WHILE 1 = 1
BEGIN
FETCH Car
INTO #CarId
IF ##FETCH_STATUS <> 0
BREAK
INSERT INTO #TEMP
EXEC spQ_GetCar(#CarId)
END
CLOSE #ServiceAgreements
DEALLOCATE #ServiceAgreements
DROP TABLE #TEMP
Notes: Performance was not relevant as it was a one off script.

Related

IF... ELSE... two mutually exclusive inserts INTO #temptable

I need to insert either set A or set B of records into a #temptable, depending on certain condition
My pseudo-code:
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF {some-condition}
SELECT {columns}
INTO #t1
FROM {some-big-table}
WHERE {some-filter}
ELSE
SELECT {columns}
INTO #t1
FROM {some-other-big-table}
WHERE {some-other-filter}
The two SELECTs above are exclusive (guaranteed by the ELSE operator). However, SQL compiler tries to outsmart me and throws the following message:
There is already an object named '#t1' in the database.
My idea of "fixing" this is to create #t1 upfront and then executing a simple INSERT INTO (instead of SELECT... INTO). But I like minimalism and am wondering whether this can be achieved in an easier way i.e. without explicit CREATE TABLE #t1 upfront.
Btw why is it NOT giving me an error on a conditional DROP TABLE in the first line? Just wondering.
You can't have 2 temp tables with the same name in a single SQL batch. One of the MSDN article says "If more than one temporary table is created inside a single stored procedure or batch, they must have different names". You can have this logic with 2 different temp tables or table variable/temp table declared outside the IF-Else block.
Using a Dyamic sql we can handle this situation. As a developoer its not a good practice. Best to use table variable or temp table.
IF 1=2
BEGIN
EXEC ('SELECT 1 ID INTO #TEMP1
SELECT * FROM #TEMP1
')
END
ELSE
EXEC ('SELECT 2 ID INTO #TEMP1
SELECT * FROM #TEMP1
')

TVP - are they used for input and output?

I've started to read the notes on Table Value Parameters
HERE ON MSDN
HERE ON SOMMARSKOG
Can these TVPs be used as input parameters and output parameters?
Is there a point in having them as an output parameter?
I get the feeling it might be possible to have a TVP as output from one stored procedure and then that feeding into another stored procedure - possible?
The syntax of the script which calls the first sproc and then calls the second sproc using the output TVP from the first is the bit I'm unsure of.
EDIT
Apologies for the confusion of my post - it seems that the initial procedures results need to go into the TVP - I thought that the TVP needed to be involved within that sproc. So a model of what I was talking about is the following - hopefully a valid use of TVPs...
CREATE TYPE myfirstTVP AS TABLE (id INT NOT NULL PRIMARY KEY);
GO --<<this sproc will find the ids (+ other fields) that need to be investigated
CREATE PROC test1 as
SELECT 1 UNION
SELECT 2 UNION
SELECT 3;
GO
GO --<<this sproc uses the found ids to do one aspect of the investigation
CREATE PROC test2
#t2 myfirstTVP READONLY
AS
SELECT id*2
FROM #t2;
GO
GO --<<this sproc uses the found ids to do another aspect of the investigation
CREATE PROC test3
#t4 myfirstTVP READONLY
AS
SELECT id*3
FROM #t4;
GO
--<<this is where the TVP is used and the sprocs are called
DECLARE #t3 myfirstTVP ;
INSERT INTO #t3
EXEC test1;
EXEC test2 #t3;
EXEC test3 #t3;
I'm not 100% sure what you want to achieve, but you can in a sense emulate behaviour of 'output' parameters ,
CREATE TYPE LIST_OF_INT AS TABLE (id int not null primary key);
GO
create procedure test1 as
begin
declare #t1 LIST_OF_INT;
insert into #t1 (id) values (1);
select * from #t1;
end;
GO
declare #t2 LIST_OF_INT ;
insert into #t2
EXEC test1;
select * from #t2;
I think you missed this bit from the MSDN link you cited.
Table-valued parameters must be passed as input READONLY parameters to
Transact-SQL routines.

After Insert Trigger....Looping

What would the processing load concern be if I had an "After Insert" trigger created on a table and in that trigger I performed a While loop to iterate through "potentially" multiple rows?
End result is I will 99.999% of the time have only 1 row, but as the future is unpredictable i also want to be able to handle multiple rows being inserted.
Trigger Model:
1) Insert information into the table
2) Create views specific to the client, via stored procedures (if possible)
What Say You? :)
Haven't fully developed but this is the design i am looking for, may not be structurally sound but should get the point acrossed.
CREATE TRIGGER dbo.New_Client_Setup
ON dbo.client
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
--Fill Temp Table
select * into #clients
from inserted
--Iterate through Temp Table
While (select count(*) from #clients) <> 0 BEGIN
declare #id int, #clnt nvarchar(10)
select top(1)
#id = id
, #clnt = short
order by id desc
Execute dbo.sp_Create_View_Client ( #id, #clnt )
-- Drop used ID
delete from #clients
where id = #id
END
Drop table #clients
END
GO
Again, observe the design of the trigger not necessarily the syntactic sugar
Design wise, reading the comments, I think you do not neccesarily need to do this in triggers. I would say you should do it as part of your insert statement in transactions - i.e. do the insert, and then do the loop that you want to do (whatever that does - execute dbo.sp_Create_View_Client)...
The second thing I would mention is what exactly is dbo.sp_Create_View_Client doing - is it a must-dependent on the insert? Meaning, what happens if the insert works fine, and the trigger fails? I would maybe do the whole insert and execute of the SP all in one transaction, so as to preserve data integrity.

Sql query returns single or multiple records?

Within my stored procedure I execute another stored procedure. Here is an example
SELECT DISTINCT ID
FROM TABLE
WHERE NAME IS NOT NULL --CAN RETURN MULTIPLE RECORDS
Then
I need to execute my stored procedure and pass parameters ID from the query above
So if the query above returns two IDs I need to execute the stored procedure below 2 times. The way I think is to create a loop but more than sure it's not the most efficient way.
EXEC StoredProcedureName ID
any ideas?
if it's possible for you use function instead stored procedure and put it right in select statment
You either need to use a loop (whether it explicitly uses DECLARE CURSOR or not is largely irrelevant), or you need to extract the logic in StoredProcedureName such that it can work on a set instead of a single value at a time.
DECLARE #value INT
DECLARE cur CURSOR FOR SELECT value FROM YourTable
open cur
FETCH NEXT FROM cur INTO #value
WHILE ##FETCH_STATUS = 0
BEGIN
exec YourProcedure #value
exec AnotherProcedure #value
FETCH NEXT FROM cur INTO #value
END
CLOSE cur
DEALLOCATE Cur
Can you alter your stored procedure?
You could perhaps incorporate the
SELECT DISTINCT ID FROM TABLE WHERE NAME IS NOT NULL
query into the stored procedure rather than passing in parameters.
You need to create a cursor and loop through it and call you procedure.
For example
DECLARE #ID int
DECLARE some_cursor FAST_FORWARD FOR
SELECT DISTINCT ID FROM TABLE WHERE NAME IS NOT NULL
OPEN some_cursor
FETCH NEXT FROM some_cursor
INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC StoredProcedureName #ID
FETCH NEXT FROM some_cursor
INTO #ID
END
CLOSE some_cursor ;
DEALLOCATE some_cursor;
As you'll notice its clumsy to write and its SQL can't optimize what ever StoredProcedureName is doing for more than one input.

Navigating the results of a stored procedure via a cursor using T-SQL

Due to a legacy report generation system, I need to use a cursor to traverse the result set from a stored procedure. The system generates report output by PRINTing data from each row in the result set. Refactoring the report system is way beyond scope for this problem.
As far as I can tell, the DECLARE CURSOR syntax requires that its source be a SELECT clause. However, the query I need to use lives in a 1000+ line stored procedure that generates and executes dynamic sql.
Does anyone know of a way to get the result set from a stored procedure into a cursor?
I tried the obvious:
Declare Cursor c_Data For my_stored_proc #p1='foo', #p2='bar'
As a last resort, I can modify the stored procedure to return the dynamic sql it generates instead of executing it and I can then embed this returned sql into another string and, finally, execute that. Something like:
Exec my_stored_proc #p1='foo', #p2='bar', #query='' OUTPUT
Set #sql = '
Declare Cursor c_Data For ' + #query + '
Open c_Data
-- etc. - cursor processing loop etc. goes here '
Exec #sql
Any thoughts? Does anyone know of any other way to traverse the result set from a stored proc via a cursor?
Thanks.
You could drop the results from the stored proc into a temp table and select from that for your cursor.
CREATE TABLE #myResults
(
Col1 INT,
Col2 INT
)
INSERT INTO #myResults(Col1,Col2)
EXEC my_Sp
DECLARE sample_cursor CURSOR
FOR
SELECT
Col1,
Col2
FROM
#myResults
Another option may be to convert your stored procedure into a table valued function.
DECLARE sample_cursor CURSOR
FOR
SELECT
Col1,
Col2
FROM
dbo.NewFunction('foo', 'bar')
You use INSERT ... EXEC to push the result of the procedure into a table (can be a temp #table or a #table variable), the you open the cursor over this table. The article in the link discusses the problems that may occur with this technique: it cannot be nested and it forces a transaction around the procedure.
You could execute your SP into a temporary table and then iterate over the temporary table with the cursor
create table #temp (columns)
insert into #temp exec my_stored_proc ....
perform cursor work
drop table #temp