Accessing temp table outside Stored Procedure - sql-server

I have Stored Procedure which gives output as temp table
Create Proc Hello (#id int,#name nvarchar(30))
as
begin
If (OBJECT_ID('tempdb..#Welcome') Is Not Null) Drop Table #Welcome
select * into #Welcome from hello where id=#id
If (OBJECT_ID('tempdb..#Welcomes') Is Not Null) Drop Table #Welcomes
select * into #Welcomes from hello where name=#name
end
Now I got 2 temp table as result which i will be using in dataset..
Now i need to access the this #welcome in another stored procedure ..I mean
Create Proc HelloThere(#ids int,#name nvarchar(10))
as
begin
exec hello #id = #ids ,#name =#name
//select * from #Welcome(Here i need to access the #Welcome so i can perform inner join something like below//
select * from #welcome inner join Atable on #welcome.id=Atable.id
end

Temporary tables created in a stored procedure are automatically dropped when the stored procedure completes, so the temp table will not be available to the calling stored procedure.
One method to keep the temp table around is to explicitly create the table in the calling proc (using CREATE TABLE or SELECT INTO) and then load it in the called proc:
CREATE PROC Hello #id int,#name nvarchar(30)
AS
INSERT INTO #Welcome SELECT * FROM hello where id=#id;
GO
CREATE PROC HelloThere(#ids int,#name nvarchar(10))
AS
If OBJECT_ID(N'tempdb..#Welcome', 'U') IS NOT NULL DROP TABLE #Welcome;
SELECT * INTO #welcome FROM hello WHERE 0 = 1;
EXEC hello #id = #ids ,#name =#name;
SELECT * FROM #welcome INNER JOIN Atable ON #welcome.id=Atable.idl
GO
As #JeroenMostert mentioned in comments, you can peruse http://www.sommarskog.se/share_data.htmlHow to Share Data between Stored Procedures for other techniques.

You can create a global temporary table (2 "#") if need to use outside of the procedure:
CREATE PROC TEST AS
SELECT TOP 10 * INTO ##GLOBAL_TABLE FROM SYS.all_objects
GO
EXEC TEST
SELECT * FROM ##GLOBAL_TABLE --It works
Just be careful that other processes that may try to use the same name and generate errors (or drop your global table and use it in another way)
You can create the table with the date you've created the procedure after (Change ##USERS to something like ##USERS_20220101)

Related

Checking whether value exists in results of a stored procedure

I have a stored procedure which returns a list of IDs for a particular set of generators I want to be able to then use the results of this stored procedure as part of another query.
Can I write a query like:
select * from table where id in (exec dbo.storedprocedurename)
Using table variable and JOIN you can achieve this. Store the procedure result into the table.
DECLARE #ProcOutput TABLE (Id INT);
INSERT INTO #ProcOutput (Id)
EXEC [dbo].[storedprocedurename]
SELECT T.*
FROM Table T
JOIN #ProcOutput O ON O.Id = T.Id
If the procedure returns multiple entries, according to the output you can re-design the table's schema.
If your output of procedure is 2 columns then you may try this:
INSERT INTO MyTable
(
Col1,
Col2
)
EXEC [dbo].[storedprocedurename]
GO
SELECT * FROM TABLE WHERE ID IN (SELECT Col1 from Mytable)

Transact-SQL: stored procedure receive generic table? [duplicate]

I creating triggers for several tables. The triggers have same logic. I will want to use a common stored procedure.
But I don't know how work with inserted and deleted table.
example:
SET #FiledId = (SELECT FiledId FROM inserted)
begin tran
update table with (serializable) set DateVersion = GETDATE()
where FiledId = #FiledId
if ##rowcount = 0
begin
insert table (FiledId) values (#FiledId)
end
commit tran
You can use a table valued parameter to store the inserted / deleted values from triggers, and pass it across to the proc. e.g., if all you need in your proc is the UNIQUE FileID's:
CREATE TYPE FileIds AS TABLE
(
FileId INT
);
-- Create the proc to use the type as a TVP
CREATE PROC commonProc(#FileIds AS FileIds READONLY)
AS
BEGIN
UPDATE at
SET at.DateVersion = CURRENT_TIMESTAMP
FROM ATable at
JOIN #FileIds fi
ON at.FileID = fi.FileID;
END
And then pass the inserted / deleted ids from the trigger, e.g.:
CREATE TRIGGER MyTrigger ON SomeTable FOR INSERT
AS
BEGIN
DECLARE #FileIds FileIDs;
INSERT INTO #FileIds(FileID)
SELECT DISTINCT FileID FROM INSERTED;
EXEC commonProc #FileIds;
END;
You can
select * into #Inserted from inserted
select * into #Deleted from deleted
and then
use these two temp tables in your stored proc
The tables inserted and deleted are only available inside the trigger. You can only use them in run-time. They will then contain the affected rows.
Also, your code might not work as expected if there is not exactly one row inserted.

SQL Server 2008 R2: Cursor with condition

I have the table which contains the table's id and name.
Now I want to retrieve the table names by passing table ID to the stored procedure.
Example:
create procedure spGetAllTables
#TableIDs varchar(max)
AS
DECLARE #Tables varchar(max)
DECLARE Cur1 CURSOR FAST_FORWARD FOR
SELECT TableName FROM TablesContainer
WHERE TableID IN (#TableIDs)
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #Tables
WHILE(##FETCH_STATUS=0)
BEGIN
PRINT(#Tables)
FETCH NEXT FROM Cur1 INTO #Tables
END
CLOSE Cur1;
DEALLOCATE Cur1;
GO
Explanation: The table TablesContainer contains all table's ID's and table's names. I just want to print all those table names which I have passed their table ID's to the stored procedure.
The procedure working fine if I pass single value to the variable #TableIDs. But If I pass multiple values like 1,2,3 to #TableIDs then its not getting entered into the cursor.
i think you don't need cursor, there is multiple way to separate string you can pass table value parameter and split the string but as per the Q requirement Reference
DECLARE #temp TABLE (Id INT)
INSERT INTO #temp VALUES (1),(2),(6)
DECLARE #TableIDs varchar(max),
#xml xml
SELECT #TableIDs = '1,2,3,4,5'
SELECT #xml = CONVERT(xml,' <root> <x>' + REPLACE(#TableIDs,',','</x> <x>') + '</x> </root> ')
SELECT id FROM #temp
WHERE id IN (
SELECT T.c.value('.','INT')
FROM #xml.nodes('/root/x') T(c)
)
You don't neet to pass the IDs as a CSV list. SQL Server has table-valued parameters since the SQL Server 2008 version at least. A TVP is a table parameter to which you insert data with plain INSERT statements in SQL, or as an collection (a DataTable or DataReader) in ADO.NET.
You can JOIN with the TVP as you would with any table or table variable.
First, you need to define the TVP type in the database:
CREATE TYPE IdTableType AS TABLE ( ID uniqueidentifier);
Then you can write a stored procedure to use it:
create procedure spGetAllTables(#TableIDs IdTableType READONLY)
AS
SELECT TableName
FROM TablesContainer
inner join #TableIDs on #TableIDs.ID=TablesContainer.TableID
To call the stored procedure from T-SQL you can create a table parameter and fill it:
declare #tableIds IdTableType;
insert into #tableIds
VALUES
('....'),
('....');
exec #spGetAllTables(#tableIds);

Sharing data between stored procedures

I have a stored procedure called dbo.Match.
It looks like this :
CREATE Procedure [dbo].[MATCH]
#parameterFromUser nvarchar(30),
#checkbool int
As
Begin
--SOME CODE
select RowId,
PercentMatch
from #Matches
End
This procedure is being called from another stored procedure :
CREATE Procedure MatchMotherFirstName
#MotherFN nvarchar(20) , #checkbool int
As begin
SELECT #constVal = FunctionWeight
FROM dbo.FunctionWeights
WHERE FunctionWeights.FunctionId = 20;
/*
Some code to execute `dbo.Match` procedure in above procedure called `MatchMotherFirstName` , retrieve `RowNumber` and `PercentMatch`,
Insert into #Temp in their respective fields , and calculate `PercentMatch * constVal`,
and insert in corresponding column called `percentage` in `#Temp`
*/
End
I need to execute dbo.Match stored procedure in above procedure, retrieve RowID and PecrntMatch value, #constval value we have above, multiply #constVal and percentmatch and store it in Percentage column of #Temp and insert results from dbo.Match procedure in a temporary table. dbo.Match returns only RowId and PercentMatch.
Structure of Temporary table:
create table #Temp
(
Rownumber int not null,
ValFromUser nvarchar(30),
ColumnName nvarchar(30),
ValFromFunc decimal(18, 4),
FuncWeight decimal(18, 4), -- #constVal here
Percentage decimal(18, 4) not null, -- calculated value here i.e (FuncWeight * ValFromFunc)
);
In #Temp, I need to insert the value of #constVal as well as calculate a column and insert i.e PercentMatch * contVal for rows inserted in this execution call only.
How can I do this in above procedure in most efficient way ?
Edit : For purpose of clarity , here is what I was doing if dbo.Match was a function and not a procedure:
if #MotherFN is not null
begin
SELECT #constVal = FunctionWeight
FROM dbo.FunctionWeights
WHERE FunctionWeights.FunctionId = 20;
INSERT INTO #Temp2
(RowNumber,ValFromUser,ColumnName,ValFromFunc,FuncWeight,percentage)
SELECT RowId,
#MotherFN ,
'mothersfirstname'
,PercentMatch,
#constVal,
PercentMatch * #constVal
FROM dbo.MatchMatch(#MotherFN, 0)
end
Like I can retrieve value of Percentmatch, #constval and multiply them both to insert in #Temp , how I may do this while I execute the dbo.Match procedure instead of calling dbo.Match function ?
You have several options, ranging from incredibly easy to overly complicated. The easiest (and most efficient) ways of doing what you describe are:
Don't do it: just include that calculation in the query. Why does it need to be in the table definition?
Add a computed column the temp table when it is created. This requires that you also include a field to store the "Constant Value" so that it can be referenced by the computed column. If the calculation is somewhat expensive and/or there will be lots of rows and frequently selected from (and possibly used in WHERE and/or ORDER BY clauses), then you can make the computed column PERSISTED so that it is calculated upon INSERT and any UPDATE that updates the fields referenced in the computed column.
Add a computed column the temp table after the table has been created. This allows for embedding the "Constant Value" into the computed column so that there is no need for a [ConstantValue] column. If the calculation is somewhat expensive and/or there will be lots of rows and frequently selected from (and possibly used in WHERE and/or ORDER BY clauses), then you can make the computed column PERSISTED so that it is calculated upon INSERT and any UPDATE that updates the fields referenced in the computed column.
P.S. Just in case you find yourself asking "why not just create the temp table dynamically in one step instead of two steps?": a local temporary table created in Dynamic SQL will cease to exist after the EXEC of that Dynamic SQL. A global temp table will survive the execution of the Dynamic SQL, but then the table name is shared across all sessions so another session executing this code at the same time would error on the name conflict. In that case you would need to generate a GUID via NEWID() to use as the global temp table name and concatenate that value as well into the Dynamic SQL, but then you are stuck being required to use Dynamic SQL for all references to the global temp table (including for the INSERT...EXEC) and that is just more work for no benefit.
Test Setup
IF (OBJECT_ID(N'tempdb..#InnerProc') IS NOT NULL)
BEGIN
DROP PROCEDURE #InnerProc;
END;
GO
IF (OBJECT_ID(N'tempdb..#TempResults1') IS NOT NULL)
BEGIN
DROP TABLE #TempResults1;
END;
IF (OBJECT_ID(N'tempdb..#TempResults2') IS NOT NULL)
BEGIN
DROP TABLE #TempResults2;
END;
IF (OBJECT_ID(N'tempdb..#TempResults3') IS NOT NULL)
BEGIN
DROP TABLE #TempResults3;
END;
GO
CREATE PROCEDURE #InnerProc
AS
SET NOCOUNT ON;
SELECT TOP 20 so.[object_id], so.[modify_date]
FROM [master].[sys].[objects] so
ORDER BY so.[modify_date] DESC;
GO
Option 1
CREATE TABLE #TempResults1
(
[ObjectId] INT NOT NULL,
[ModifyDate] DATETIME NOT NULL
);
DECLARE #ConstantValue1 INT;
SET #ConstantValue1 = 13;
INSERT INTO #TempResults1 ([ObjectId], [ModifyDate])
EXEC #InnerProc;
SELECT 1 AS [Test], *, DATEADD(DAY, #ConstantValue1, [ModifyDate]) AS [SomeCalculation]
FROM #TempResults1;
Option 2
CREATE TABLE #TempResults2
(
[ObjectId] INT NOT NULL,
[ModifyDate] DATETIME NOT NULL,
[ConstantValue] INT NULL, -- will be added via UPDATE
[SomeCalculation] AS (DATEADD(DAY, [ConstantValue], [ModifyDate])) -- PERSISTED ??
);
INSERT INTO #TempResults2 ([ObjectId], [ModifyDate])
EXEC #InnerProc;
SELECT 2 AS [Test], * FROM #TempResults2;
UPDATE #TempResults2
SET [ConstantValue] = 13;
SELECT 2 AS [Test], * FROM #TempResults2;
Option 3
DECLARE #ConstantValue3 INT;
SET #ConstantValue3 = 13;
CREATE TABLE #TempResults3
(
[ObjectId] INT NOT NULL,
[ModifyDate] DATETIME NOT NULL
);
INSERT INTO #TempResults3 ([ObjectId], [ModifyDate])
EXEC #InnerProc;
SELECT 3 AS [Test], * FROM #TempResults3;
-- The next 3 lines could be done just after the CREATE TABLE and before the INSERT,
-- but doing it now allows for seeing the "before" and "after" with the data.
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N'ALTER TABLE #TempResults3 ADD [SomeCalculation] AS (DATEADD(DAY, '
+ CONVERT(NVARCHAR(10), #ConstantValue3) + N', [ModifyDate])); --PERSISTED';
EXEC (#SQL);
SELECT 3 AS [Test], * FROM #TempResults3;
Well, in general terms there is no value on creating complex logics if you just need to do simple stuffs. In the scenario that you described I would tend to think the best approach would be the use of phisical table that can be accessed any time by either the dbo.Match and dbo.MatchMotherFirstName procedures. If you don't want to leave it in the database after logic execution, use the CREATE/DROP sentences to CREATE/DROP the table as per your needs.
You have 3 Easy enough options. One has a decent sized performance hit, One requires a config update on the server and One requires a change to the match stored procedure.
Option 1
In the MatchMotherFirstName procedure declare a table for the Match results.
CREATE TABLE #tmpMatchResults (Col1 , Col2....)
Insert into #tmpMatchResults
EXEC [dbo].[MATCH]
This has a performance hit, but it works without any changes to the Match proc code or server config. If you only expect very few lines, this will work just fine
Option 2
Use OpenRowSet or OpenQuery
Select * FROM OPENROWSET(connection,'Exec database.dbo.MATCH')
This requires a config change to allow data access
Option 3
Update the MATCH Stored Procedure to push the results to a temp table
CREATE Procedure [dbo].[MATCH]
--SOME CODE
select RowId, PercentMatch from #tmpMatches
Be sure NOT to drop the temp table at the end of the proc
Then In your MatchMotherFirstName procedure, while the session is active, you can call the proc
EXEC dbo.MATCH #param
and the result set with
SELECT * FROM #tmpMatches
Some people would argue that you should clean up (drop table) the temp table at the end of the MATCH proc call. You can include a parameter in the MATCH proc to persist results or do table cleanup.

SQL Server, Insert into table from select query?

I would fill a table from a query in a stored procedure,
This works:
SELECT *
INTO #tmpTable
FROM MyTable
This works:
SELECT TOP (1) *
FROM MyTable
WHERE Land = #Land
but how do I fill #tmpTable with
SELECT TOP (1) *
FROM MyTable
WHERE Land = #Land
Because the #temp Table's scope is limited to its session (SPID), i.e. the Stored Procedure itself. After the SP execution completes the #temp table is Dropped.
Also while the SP is being executed you cannot see the #temp Table from other sessions (SPID)
USE Global temp table like ##temp even it can be accessible after execution of sp also

Resources