Dynamically create temp table based on resultset from SP - sql-server

I have a SP that calls another SP and the resultset needs to be filtered to only the columns that I am interested in.
DECLARE #myTable TABLE
(
Field1 INT,
Field2 INT,
Field3 INT
)
--If someSP returns say 30 params and I need only 3 params, I don't want to declare all 30 in my temp table #myTable
INSERT INTO #myTable
(Field1, Field2, Field3)
EXEC someSP --Need to selectively filter recordset from SP
#InputParam1 = 'test'
If I cannot do this, I would want to create the temp table DYNAMICALLY based on the resultset from someSP (This way it relieves maintenance issues when someSP is modified to add a new param, I dont need to modify this proc as well

Short answer: no, you can't do that.
You have to pre-declare your temp table with the exact number of columns that will be returned from the stored proc.
The workaround is to use persistent tables. For example, you could have a permanent table in your database called someSPResults. Whenever someSP is changed to have a different number of output columns, change the format of someSPResults as part of the deployment.
Then you can either do this:
insert into dbo.someSPresults
exec someSP
Or inside someSP, you can have the results be inserted directly into the someSPresults table as a normal part of execution. You just have to make sure to identify exactly which records in the someSPresults table came from each execution of someSP, because that stored proc could be fired multiple times simultaneously, thereby dumping a lot of data into someSPresults.

cmsjr stated, "A table variable cannot be the target of a result set from another stored procedure."
I thought that was true too, but then I tested it. This code works in both 2005 and 2008:
CREATE PROCEDURE someSP (#InputParam1 varchar(100)) AS
SELECT LEN(#InputParam1), DATALENGTH(#InputParam1), ##SPID
GO
DECLARE #myTable TABLE (
Field1 INT,
Field2 INT,
Field3 INT
)
INSERT INTO #myTable (Field1, Field2, Field3)
EXEC someSP
#InputParam1 = 'test'
SELECT * FROM #myTable
I knew that would work with #temp tables, but I thought it would not work with #temp tables.
That doesn't answer DotnetDude's question though.

A table variable cannot be the target of a result set from another stored procedure, also you can't perform DDL on table variables after they are declared, they will always have the same definition they were declared with. Temp table is your best bet.

I could think of two options, but I didn't have time to test them: convert the SP into an User Defined Function and use the SELECT * FROM {function} INTO {table}, or use OPENROWSET:
SELECT *
FROM OPENROWSET('SQLOLEDB',
'servername';'username';'password',
'exec dbname.{owner}.yourstoredproc') AS spResult
INTO {tablename}
Both solutions should create the table on the fly, then you can simply select from it.

Based on the comments above, I'd suggest you consider a table valued function.
This can be parameterised and you can do this:
INSERT #foo (col1, col14, col29)
SELECT col1, col14, col29 FROM dbo.ufnTVF (#p1, #p2)
Otherwise, it's OPENROWSET as the "cleanest" (I use this loosely) solution
Or, you modify the resultset of your stored proc to onlky return the columns you want.
This implies dynamic SQL or lots of IF statements. Which in some circumstances will not parse correctly (with SET FMTONLY etc).
You could be trying to code against a 3rd party app or system stored procs (we don't have full details), but it feels messy and wrong. SQL Server 2005 has a huge number of DMVs and catalogue (or catalog depending on which side of the Atlantic you are) views that remove the need for system proc calls.
If you're trying to mimic some aspects of OO design (one proc to do something for everyboy), then I wouldn't. If you need a query that retruns 3 columns of 30, then do so. This will run far better because unused tables and columns will be ignored on in the plan, indeed do not need included.

Related

Use output of procedure in a new procedure SQL Server [duplicate]

I'm not sure if this is something I should do in T-SQL or not, and I'm pretty sure using the word 'iterate' was wrong in this context, since you should never iterate anything in sql. It should be a set based operation, correct? Anyway, here's the scenario:
I have a stored proc that returns many uniqueidentifiers (single column results). These ids are the primary keys of records in a another table. I need to set a flag on all the corresponding records in that table.
How do I do this without the use of cursors? Should be an easy one for you sql gurus!
This may not be the most efficient, but I would create a temp table to hold the results of the stored proc and then use that in a join against the target table. For example:
CREATE TABLE #t (uniqueid int)
INSERT INTO #t EXEC p_YourStoredProc
UPDATE TargetTable
SET a.FlagColumn = 1
FROM TargetTable a JOIN #t b
ON a.uniqueid = b.uniqueid
DROP TABLE #t
You could also change your stored proc to a user-defined function that returns a table with your uniqueidentifiers. You can joing directly to the UDF and treat it like a table which avoids having to create the extra temp table explicitly. Also, you can pass parameters into the function as you're calling it, making this a very flexible solution.
CREATE FUNCTION dbo.udfGetUniqueIDs
()
RETURNS TABLE
AS
RETURN
(
SELECT uniqueid FROM dbo.SomeWhere
)
GO
UPDATE dbo.TargetTable
SET a.FlagColumn = 1
FROM dbo.TargetTable a INNER JOIN dbo.udfGetUniqueIDs() b
ON a.uniqueid = b.uniqueid
Edit:
This will work on SQL Server 2000 and up...
Insert the results of the stored proc into a temporary table and join this to the table you want to update:
INSERT INTO #WorkTable
EXEC usp_WorkResults
UPDATE DataTable
SET Flag = Whatever
FROM DataTable
INNER JOIN #WorkTable
ON DataTable.Ket = #WorkTable.Key
If you upgrade to SQL 2008 then you can pass table parameters I believe. Otherwise, you're stuck with a global temporary table or creating a permanent table that includes a column for some sort of process ID to identify which call to the stored procedure is relevant.
How much room do you have in changing the stored procedure that generates the IDs? You could add code in there to handle it or have a parameter that lets you optionally flag the rows when it is called.
Use temporary tables or a table variable (you are using SS2005).
Although, that's not nest-able - if a stored proc uses that method then you can't dumpt that output into a temp table.
An ugly solution would be to have your procedure return the "next" id each time it is called by using the other table (or some flag on the existing table) to filter out the rows that it has already returned
You can use a temp table or table variable with an additional column:
DECLARE #MyTable TABLE (
Column1 uniqueidentifer,
...,
Checked bit
)
INSERT INTO #MyTable
SELECT [...], 0 FROM MyTable WHERE [...]
DECLARE #Continue bit
SET #Continue = 1
WHILE (#Continue)
BEGIN
SELECT #var1 = Column1,
#var2 = Column2,
...
FROM #MyTable
WHERE Checked = 1
IF #var1 IS NULL
SET #Continue = 0
ELSE
BEGIN
...
UPDATE #MyTable SET Checked = 1 WHERE Column1 = #var1
END
END
Edit: Actually, in your situation a join will be better; the code above is a cursorless iteration, which is overkill for your situation.

How best to reuse the same stored procedure for details and summary

Given a stored procedure like this:
create procedure BigParameterizedSearch(#criteria xml, #page int, #pageSize int)
as
...lots of populating table variables & transforming things...
select ..bunch of columns..
from ..bunch of tables..
where ..bunch of (#has_some_filter=0 or ..some filter criteria..) and..
order by ..big case statement depends on criteria..
offset (#page-1)*#pageSize rows
fetch next #pageSize rows only
option (recompile)
and its 'summary' counterpart:
create procedure BigParameterizedSearchSummary(#criteria xml, #page int, #pageSize int)
as
...same exact populating table variables & transforming things...
select groupCol, ..various aggregates..
from ..same exact bunch of tables..
where ..same exact bunch of (#has_some_filter=0 or ..some filter criteria..) and..
group by groupCol
order by ..smaller case statement depends on criteria..
offset (#page-1)*#pageSize rows
fetch next #pageSize rows only
option (recompile)
The two stored procedures are largely the same, only the select clause and order by clause is different, and the 'summary' version adds a group by.
Now, the question is, how could these two stored procedures be combined? Or, how otherwise could the duplicate code be avoided? I have tried in vain to create a common table-valued-function with returns table as return so that the select and group by could be pulled out to calling stored procedures without impacting performance. The restrictions on returns table as return makes it too difficult to perform all of the complicated setup, and if I make it populate a table variable then the full result set is populated for each page which slows it down too much. Is there any other strategy aside from going full dynamic SQL?
You could use a view that contains all the columns from all the tables with all the filters you would need for both queries and then do your selects and groupings against that view. That would save you on the issue of redundancy if I understand your situation correctly. Honestly this sort of issue is exactly what views are for.
Also, I am just curious, do you really need to recompile every time? Is there something incredible going on that you can't afford to use a cached execution plan?
Could you not just split this into a sproc that populates the tables and a sproc that calls the table population using the same parameters and then queries the tables?
create table Prepop (a int)
go
create procedure uspPopulate (#StartNum int)
as
truncate table Prepop
insert into Prepop values
(#StartNum)
,(#StartNum+1)
,(#StartNum+2)
,(#StartNum+3)
go
create procedure uspCall (#StartNum int, #Summary bit)
as
exec uspPopulate #StartNum = #StartNum
if #Summary = 1
select avg(a) as Avga
from Prepop
else
select a
from Prepop
go
exec uspCall #StartNum = 6, #Summary = 1
exec uspCall #StartNum = 6, #Summary = 0

SQL Server 2008, can I refer to a temporary table in a select statement within a udf?

I'm trying to run a select query on a temporary table within a udf. I can't find documentation stating this isn't allowed, yet the below stored procedure won't compile when I change tblDailyPricingAndVol to #dailyPricingAndVolBySymbol (my temporary table of course. The temp table is created at a higher level (in a stored procedure before the stored procedure that uses this function) if that affects anything... thanks in advance.
Edit:
The udf is meant to just be a helper for the stored procedure that calls it.. I'm trying to query a temporary table with it due to the fact that it'll get called thousands of times each time it runs. The data that it retrieves and then aggregates is in a table with millions of rows. So I pare down the data into several hundred records, into the temporary table. This will speed the function up dramatically, even though it'll still take a fair bit of time to run.
ALTER FUNCTION dbo.PricingVolDataAvailableToDateProvided
(#Ticker nchar(10),
#StartDate DATE,
#NumberOfDaysBack int)
RETURNS nchar(5)
AS
BEGIN
DECLARE #Result nchar(5)
DECLARE #RecordCount int
SET #RecordCount = (SELECT COUNT(TradeDate) AS Expr1
FROM (SELECT TOP (100) PERCENT TradeDate
FROM tblDailyPricingAndVol WHERE (Symbol = #Ticker) AND (TradeDate IN
(SELECT TOP (#NumberOfDaysBack) CAST(TradingDate AS DATE) AS Expr1
FROM tblTradingDays
WHERE (TradingDate <= #StartDate)
ORDER BY TradingDate DESC))
ORDER BY TradeDate DESC) AS TempTable)
IF #RecordCount = #NumberOfDaysBack
SET #Result = 'True'
ELSE
SET #Result = 'False'
RETURN #Result
END
As been mentioned by other posters, you can't use a temporary table in an UDF. What you can do is pass a User-Defined Table to your function.
User-Defined Table Types
In SQL Server 2008, a user-defined table type is a user-defined type
that represents the definition of a table structure. You can use a
user-defined table type to declare table-valued parameters for stored
procedures or functions, or to declare table variables that you want
to use in a batch or in the body of a stored procedure or function.
A quick fix for changing your code could be
CREATE TYPE DailyPricingAndVolBySymbolType AS TABLE (<Columns>)
DECLARE #DailyPricingAndVolBySymbol DailyPricingAndVolBySymbolType
INSERT INTO #DailyPricingAndVolBySymbol SELECT * FROM #DailyPricingAndVolBySymbol
ALTER FUNCTION dbo.PricingVolDataAvailableToDateProvided (
#DailyPricingAndVolBySymbol DailyPricingAndVolBySymbolType READONLY
#Ticker nchar(10),
#StartDate DATE,
#NumberOfDaysBack int
) ...
Looks like you're out of luck. I created a quick function below and got an explicit compiler message that says you can't reference temp tables in a function. I'm not sure why you would need to reference temp tables within a UDF, that's not really the spirit of UDF. Could you show how you were planning to call this UDF? Maybe we could help on that refactor.
Temp tables cannot be accessed from within a function. I suggest using a staging table instead. To better organize these in your DB you could create a schema called Staging, a table called Staging.dailyPricingAndVolBySymbol, and call that from your UDF.

Can the results of a stored procedure be sorted with T-SQL?

I am operating on a MSSQL 2005 database system that has many stored procedures. One of the stored procedures is a "report" and I want to sort it without editing the stored procedure. Is there a way to sort the results of a stored procedure on the fly : something like this:
exec spReport
order by ColumnT
You can insert into a temporary table. Then, sort from table.
e.g.
INSERT INTO #table
EXEC spReport
SELECT *
FROM #table
ORDER BY ColumnT
No, you cannot do this. If you know the structure of the resultset, you can build a #tmp table first, insert #tmp exec spReport, then select from the #tmp table with an order by clause. You may even be able to hack something together using OPENQUERY or OPENROWSET, but I would recommend editing spReport either to always use the order you want or to take a parameter and define the order based on the parameter.
You should fill a temporary table with the result and then sort it. Here is the link showing how to do the first part:
How to SELECT * INTO temp table FROM Stored Procedure

Is it possible to use a Stored Procedure as a subquery in SQL Server 2008?

I have two stored procedures, one of which returns a list of payments, while the other returns a summary of those payments, grouped by currency. Right now, I have a duplicated query: the main query of the stored procedure that returns the list of payments is a subquery of the stored procedure that returns the summary of payments by currency. I would like to eliminate this duplicity by making the stored procedure that returns the list of payments a subquery of the stored procedure that returns the summary of payments by currency. Is that possible in SQL Server 2008?
You are better off converting the first proc into a TABLE-VALUED function. If it involves multiple statements, you need to first define the return table structure and populate it.
Sample:
CREATE proc getRecords #t char(1)
as
set nocouut on;
-- other statements --
-- final select
select * from master..spt_values where type = #t
GO
-- becomes --
CREATE FUNCTION fn_getRecords(#t char(1))
returns #output table(
name sysname,
number int,
type char(1),
low int,
high int,
status int) as
begin
-- other statements --
-- final select
insert #output
select * from master..spt_values where type = #t
return
end;
However, if it is a straight select (or can be written as a single statement), then you can use the INLINE tvf form, which is highly optimized
CREATE FUNCTION fn2_getRecords(#t char(1))
returns table as return
-- **NO** other statements; single statement table --
select * from master..spt_values where type = #t
The second proc simply selects from the first proc
create proc getRecordsByStatus #t char(1)
as
select status, COUNT(*) CountRows from dbo.fn2_getRecords(#t)
group by status
And where you used to call
EXEC firstProc #param
to get a result, you now select from it
SELECT * FROM firstProc(#param)
You can capture the output from a stored procedure in a temp table and then use the table in your main query.
Capture the output of a stored procedure returning columns ID and Name to a table variable.
declare #T table (ID int, Name nvarchar(50))
insert into #T
exec StoredProcedure
Inserting the results of your stored proc into a table variable or temp table will do the trick.
If you're trying to reuse code in SQL Server from one query to the next, you have more flexibility with Table Functions. Views are all right if you don't need to pass parameters or use any kind of flow control logic. These may be used like tables in any other function, procedure, view or t-sql statement.
If you made the procedure that returns the list into a table-valued function, then I believe you could use it in a sub-query.
I would use a view, unless it needs to be parameterized, in which case I would use an inline table-valued function if possible, unless it needs to be a multi-statement operation, where you can still use a table-valued function, but they are usually less efficient.

Resources