T-SQL query gone wrong exec not in table - sql

I'm trying to build a query which puts his output in a table.
The exec(#inloop_query) doesn't know a declared table from before.
(that part between the ------------------
Is this possible or do I try to do something that doesn't work?
please advise.
(The error I've is : Must declare the table variable "#inloop_table". Severity 15 State 2)
DECLARE #frame_db_name VARCHAR(max)
DECLARE #frame_db_id INT
DECLARE #frame_table TABLE (
db_id INT ,
names VARCHAR(max))
DECLARE #frame_count INT
DECLARE #frame_count_max INT
SET #frame_count = 1
SET #frame_count_max = 0
SELECT #frame_count_max = count (name) FROM sys.databases WHERE Name LIKE 'B%' and state_desc = 'online'
INSERT INTO #frame_table SELECT database_id , name FROM sys.databases WHERE Name LIKE 'B%' and state_desc = 'online' ORDER BY database_id
DECLARE #inloop_query VARCHAR(max)
DECLARE #Inloop_table TABLE (
IL_SchemaName VARCHAR(max) ,
IL_TableName VARCHAR(max) ,
IL_IndexName VARCHAR(max) ,
IL_IndexID INT ,
IL_Fragment INT)
IF #frame_count_max <= 0
PRINT '#count_max (<=0) = ' + CAST(#frame_count_max AS VARCHAR)
ELSE
WHILE #frame_count <= #frame_count_max
BEGIN
SELECT #frame_db_name = names , #frame_db_id = db_id FROM #frame_table WHERE db_id IN (SELECT TOP 1 db_id FROM #frame_table ORDER BY db_id)
PRINT '#count_max (>=0) = ' + CAST(#frame_count_max AS VARCHAR)
PRINT '#count = ' + CAST(#frame_count AS VARCHAR(max))
PRINT 'current DB name = ' + CAST(#frame_db_name AS VARCHAR(max))
PRINT 'current DB ID = ' + CAST(#frame_db_id AS VARCHAR(max))
------------------------------------------------------------
SET #inloop_query = '
USE ' + CAST(#frame_db_name AS VARCHAR(max)) +
' INSERT INTO #inloop_table
SELECT SCHEMA_NAME(o.schema_id) AS SchemaName,
OBJECT_NAME(a.object_id) AS TableName,
i.name AS IndexName,
a.index_id AS IndexID,
convert(tinyint,a.avg_fragmentation_in_percent) AS [Fragment]
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL,NULL, ''LIMITED'') AS a
INNER JOIN sys.indexes i ON i.index_id = a.index_id
AND i.object_id = a.object_id
INNER JOIN sys.objects o ON a.object_id = o.object_id
ORDER BY SchemaName, TableName, IndexID'
EXEC(#inloop_query)
------------------------------------------------------------
SET #frame_count = #frame_count + 1
DELETE FROM #frame_table WHERE db_id IN (SELECT TOP 1 db_id FROM #frame_table ORDER BY db_id)
END

#inloop_table is declared outside your #inloop_query; when the latter is executed, it has no idea about this variable. How about using an actual table?
/* comment this out:
DECLARE #inloop_query VARCHAR(max)
DECLARE #Inloop_table TABLE (
IL_SchemaName VARCHAR(max) ,
IL_TableName VARCHAR(max) ,
IL_IndexName VARCHAR(max) ,
IL_IndexID INT ,
IL_Fragment INT)
*/
-- Create an auxiliary table
CREATE TABLE InLoop_Table (
IL_SchemaName VARCHAR(max) ,
IL_TableName VARCHAR(max) ,
IL_IndexName VARCHAR(max) ,
IL_IndexID INT ,
IL_Fragment INT
);
-- ... And use this table in your dynamic sql:
SET #inloop_query = '
USE ' + CAST(#frame_db_name AS VARCHAR(max)) +
' INSERT INTO InLoop_Table ...
-- Finally, clean up:
DROP TABLE InLoop_Table;

The scope of a table variable is specific to a batch, so since your dynamic sql executes as a new batch it falls out of scope and is unrecognised. You could of course declare it within your dynamic sql, but that would be pretty pointless as you couldn't access it later. You have two decent choices:
You can put the insert outside of the sql, e.g.
DECLARE #inloop_query NVARCHAR(MAX) = 'USE Master; SELECT 1, 2, 3;';
DECLARE #inloop_table TABLE (A INT, B INT, C INT);
INSERT #inloop_table
EXEC(#inloop_query);
SELECT * FROM #inloop_table;
Or you can use a temporary table, rather than a table variable. A temporary table has session scope, so is still recognised with EXEC():
CREATE TABLE #inloop_table (A INT, B INT, C INT);
DECLARE #inloop_query NVARCHAR(MAX) = 'USE Master; INSERT #inloop_table SELECT 1, 2, 3;';
EXEC(#inloop_query);
SELECT * FROM #inloop_table;
I would also recommend using a properly declared cursor rather than a WHILE loop iterating through a table variable. The key aspect here being properly defined. Often people just use DECLARE .. CURSOR FOR SELECT.. and the default options are much slower and more memory consuming than if you tell the cursor that you won't be making updates, won't be moving backwards etc.
DECLARE DBCursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT database_id , name
FROM sys.databases
WHERE Name LIKE 'B%' and state_desc = 'online'
ORDER BY database_id;
OPEN DBCursor;
FETCH NEXT FROM DBCursor INTO #frame_db_id, #frame_db_name;
WHILE ##FETCH_STATUS = 0
BEGIN
-- DO WHATEVER YOU NEED WITH EACH DB
FETCH NEXT FROM DBCursor INTO #frame_db_id, #frame_db_name;
END
CLOSE DBCursor;
DEALLOCATE DBCursor;
One final comment, is that I always perfer sp_executesql over EXEC() and this article pretty much covers why, in this instance it doesn't make much difference, but it is worth noting.

Related

Stored procedure table-valued variable without aliases in query string must declare scalar variable

I will pass a table-valued input parameter into a stored procedure, and also a variable that contains query string, so I made my sproc like this.
CREATE PROCEDURE [dbo].[SP_SelectData_View]
(
#Sort VARCHAR(MAX),
#CONDITION VARCHAR(MAX) = ''
#Values dbo.FlowStatus READONLY
)
AS
BEGIN
DECLARE #STRQUERY NVARCHAR(MAX)
IF #CONDITION IS NOT NULL AND #CONDITION != ''
BEGIN
SET #CONDITION = 'WHERE ' + #CONDITION
END
ELSE
BEGIN
SET #CONDITION = ''
END
IF #Sort IS NULL OR #Sort = ''
BEGIN
SET #Sort = 'Id Desc'
END
BEGIN
SET #STRQUERY = 'SELECT A.*
FROM ' + #Values + ' as FlowStatus'
JOIN Tbl_A as A
ON A.status = FlowStatus.StatusNowId AND B.flow = FlowStatus.FlowNowId
' + #CONDITION + '
Order By ' + #Sort
EXEC(#STRQUERY)
END
END
But in the code above, I got an error
must declare scalar variable #Values
I've searched for it and I think it is because the aliases is not detected because it's inside a string. But if I didn't put it in a string query, the #condition and #sort variable will be error. Is there a solution where I can do both calling the table-valued variable and query string variable together?
There are several things wrong with the approach you currently have, as I and others have commented, Brent Ozar has a good reference on dynamic SQL https://www.brentozar.com/sql/dynamic/
I would say don't pass in some SQL, construct it in the stored proc; passing in parameters such as name which is used in the where, hence I have put a full working example. This also shows how to pass the user defined table type into the stored proc and then also pass it into the dynamic SQL.
I hope this is a good enough example of the techniques, I had a bit of time so thought I would try and help as much as possible :)
/*
--------------------------------------------
Create a test table to run the stored proc against
*/
IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'MyTestTable'))
BEGIN
PRINT 'Creating table MyTestTable'
CREATE TABLE [dbo].[MyTestTable](
Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
[Name] NVARCHAR(50) NOT NULL
)
INSERT INTO dbo.MyTestTable ([Name])
VALUES ('Andrew'),
('Bob'),
('john')
-- SELECT * FROM MyTestTable
END
GO
/*
--------------------------------------------
Create the table type that we pass into the store proc
*/
IF NOT EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = 'FlowStatus')
BEGIN
PRINT 'Creating type [dbo].[FlowStatus]'
CREATE TYPE [dbo].FlowStatus AS TABLE (
MyId BIGINT PRIMARY KEY,
SomeText NVARCHAR(200)
)
END
GO
/*
--------------------------------------------
Create the stored proc with the User Defined table type
*/
CREATE OR ALTER PROCEDURE [dbo].[MyStoredProc]
(
#SortBy VARCHAR(50),
#SearchName VARCHAR(50),
#Values dbo.FlowStatus READONLY
)
AS
BEGIN
-- As your SQL gets more complex it is an idea to create seperate parts of the SQL
DECLARE #SqlToExecute NVARCHAR(MAX)
-- The primary data you want to get
SET #SqlToExecute = N'
SELECT T.Id, T.[Name], V.SomeText
FROM MyTestTable AS T
LEFT JOIN #Values AS V ON V.MyId = T.Id
WHERE 1 = 1' -- We do this so that we can have many AND statements which could be expanded upon
IF #SearchName IS NOT NULL
BEGIN
SET #SqlToExecute = #SqlToExecute + N'
AND T.[Name] LIKE ''%' + #SearchName + ''''
END
IF #SortBy IS NOT NULL
BEGIN
SET #SqlToExecute = #SqlToExecute + N'
ORDER BY ' +
CASE WHEN #SortBy LIKE 'Name%' THEN N'T.[Name]'
ELSE N'T.[Id]'
END
END
-- Print out the script that will be run, useful for debugging you code
PRINT #SqlToExecute
EXEC sp_executesql #SqlToExecute,
N'#Values dbo.FlowStatus READONLY', #Values
END
GO
/*
--------------------------------------------
Now lets test it
-- Test Andrew
*/
DECLARE #flowStatusType AS dbo.FlowStatus
INSERT INTO #flowStatusType(MyId, SomeText)
VALUES(1, 'Test1'),
(2, 'Test2')
EXEC [dbo].[MyStoredProc] #SearchName = 'Andrew', #SortBy = 'Name', #Values = #flowStatusType
GO
-- Test Bob
DECLARE #flowStatusType AS dbo.FlowStatus
INSERT INTO #flowStatusType(MyId, SomeText)
VALUES(1, 'Test1'),
(2, 'Test2')
EXEC [dbo].[MyStoredProc] #SearchName = 'Bob', #SortBy = 'Name', #Values = #flowStatusType
GO
Its also worth noting that if you can just join on the #Values without needing dynamic SQL then that is sure to be less work.

retrieving all Foreign Keys and their records

What I'm trying to achieve is the following.
When I delete a record I want to check if there are any FK relationships and it needs to be recursive. That way I can display a list of all records that are related to the one you want to delete.
So a small example of nested links
project 1 -> phase 1 -> block 1 -> ..
So when I try to delete project 1 I need to get a list of the items you need to delete first:
phase 1
block 1
....
I wanted to do this with a stored procedure that takes an ID and a tablename (format [chema].[tablename]) and finds all these linked records.
The problem I'm having is with the recursive part.
Here's my code so far:
ALTER PROCEDURE core.usp_CanBeDeleted
#entityId int,
#entityName nvarchar(250)
AS
BEGIN
DECLARE #NumberRecords int, #RowCount int
DECLARE #childId int
DECLARE #query nvarchar(max)
DECLARE #eName nvarchar(250) , #keyName nvarchar(250)
DECLARE #columnName nvarchar(250)
DECLARE #keys TABLE(
RowID int IDENTITY(1, 1),
name nvarchar(250),
entityName nvarchar(250),
columnName nvarchar(250)
)
if not exists (select * from sysobjects where name='partialResults' and xtype='U')
BEGIN
CREATE TABLE partialResults(
RowID int IDENTITY(1, 1),
id int,
parentId int,
name nvarchar(250),
FK_name nvarchar(250)
)
END
DECLARE #recusiveResults TABLE(
RowID int,
id int,
parentId int,
name nvarchar(250),
FK_name nvarchar(250)
)
DECLARE #results TABLE(
RowID int,
id int,
parentId int,
name nvarchar(250),
FK_name nvarchar(250)
)
SET #RowCount = 1
-- get all FK's of the entity
INSERT INTO #keys
SELECT name, '[' + OBJECT_SCHEMA_NAME(parent_object_id) + '].[' + OBJECT_NAME(parent_object_id)+ ']',cu.column_name
from sys.foreign_keys k
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
ON k.name = CU.CONSTRAINT_NAME
where k.referenced_object_id = OBJECT_ID(#entityName)
-- set variable to number of records in temp table
SET #NumberRecords = ##ROWCOUNT
-- loop through the FK's an get all linked entities
WHILE(#RowCount <= #NumberRecords)
BEGIN
SELECT #keyName = name, #eName = entityName, #columnName = columnName
FROM #keys
WHERE RowId = #RowCount
-- get all FK information
SET #query = 'INSERT INTO partialResults(FK_name, name, id, parentId)'
+ ' SELECT ''' + #keyName + ''','''+ #eName + ''',' + 'id,' + cast(#entityId as varchar(25)) + ' as parentid'
+ ' FROM ' +#eName
+ ' WHERE id in '
+ ' (SELECT ' + #columnName
+ ' FROM ' + #entityName
+ ' WHERE id = ' + cast(#entityId as varchar(25))
+ ' )'
--print #query
EXEC (#query)
SET #RowCount = #RowCount + 1
END
-- rest number of records
SET #RowCount = 1
SELECT #NumberRecords = count(id)
FROM partialResults
-- save partialResults
INSERT INTO #results--(FK_name, name, id, parentId)
SELECT *--FK_name, name, id, parentId
FROM partialResults
DELETE FROM partialResults
WHILE(#RowCount <= #NumberRecords)
BEGIN
-- select next row
SELECT #childId = id, #eName = name
FROM #results
WHERE RowId = #RowCount
INSERT INTO #recusiveResults
EXEC core.usp_CanBeDeleted #childId, #eName
SET #RowCount = #RowCount + 1
END
INSERT INTO #results
SELECT *
FROM #recusiveResults
if exists (select * from sysobjects where name='partialResults' and xtype='U')
BEGIN
-- remove temp tables
DROP TABLE partialResults
END
-- return results
SELECT *
FROM #results
END
GO
the problem lies here:
INSERT INTO #recusiveResults
EXEC core.usp_CanBeDeleted #childId, #eName
Apparantly you can't nest an insert exec.
however I don't really see any other way to do it.
I've tried converting it into a function but then there are other problems like the dynamic query.
Any help would be greatly apreciated.
Split the procedure into an outer and an inner procedure.
In the outer procedure create a #results temp-table and then call the inner procedure.
In the inner procedure put all the logic including the recursion, but instead of selecting out the result at the end insert the result into the already existing #results table.
That way you safe a lot of time because you dont have to move data around as much. You also don't have to nest INSERT...EXEC anymore.
You also don't need the dbo.PartialResults table anymore as you can write directly into the #results table within the dynamic statement. If you still need it, to make the recursion work replace it with a #partialResults temp table that you create in the inner procedure (DON'T check for existence, just create the new one. See http://sqlity.net/en/1109/temp-tables-scoping-eclipsing/ for an explanation of temp table scoping). That way each execution is creating its own temp table and you don't have to deal with the clean-up. This is also a little less heavy compared to using a real table.
Finally, all the table variables can go too.
At the end of the inner procedure you can then do a simple SELECT * FROM #results; to output all the collected results.

How to iterate through each row in sql server?

My query returns 26 table names.
select name from sys.tables where name like '%JPro_VP_Service%'
Now I'm trying to write a query to check in every table return from the above query.
--consider this is my first table
select * from JPro_VP_Service
where row_id like '%1-101%' or row_id like '%1-102%'
-- likewise I want to search in 26 tables return from above query
I think I need to write for or cursor to accomplish this.
Can anyone help me how to achieve this?
The easiest way to do this is
Try this:
SELECT 'select * from ' + name
+ ' where row_id like ''%1-101%'' or row_id like ''%1-102%'''
FROM sys.tables
WHERE name LIKE '%JPro_VP_Service%'
you will get all tables together with the same conditions. You could execute them together.
Yes, you would have to use a cursor for this, and probably also dynamic sql
Also see
Generate dynamic SQL statements in SQL Server
Dynamic SQL PROs & CONs
DECLARE #mn INT
DECLARE #mx INT
DECLARE #tblname VARCHAR(100);
WITH cte
AS (SELECT Row_number()
OVER (
ORDER BY (SELECT 0)) AS rn,
name
FROM sys.tables
WHERE name LIKE '%JPro_VP_Service%')
SELECT #mn = Min(rn),
#mx = Max(rn)
FROM cte
WHILE( #mn >= #mx )
BEGIN
SELECT #tblname = name
FROM cte
WHERE rn = #mn
SELECT *
FROM #tblname
WHERE row_id LIKE '%1-101%'
OR row_id LIKE '%1-102%'
--Do something else
SET #mn=#mn + 1
END
This route may work, though you might want the results saved to a table:
DECLARE #tables TABLE(
ID INT IDENTITY(1,1),
Name VARCHAR(100)
)
INSERT INTO #tables (Name)
SELECT name
FROM sys.tables
WHERE name like '%JPro_VP_Service%'
DECLARE #b INT = 1, #m INT, #table VARCHAR(100), #cmd NVARCHAR(MAX)
SELECT #m = MAX(ID) FROM #tables
WHILE #b <= #m
BEGIN
SELECT #table = Name FROM #tables WHERE ID = #b
SET #cmd = 'select * from ' + #table + '
where row_id like ''%1-101%'' or row_id like ''%1-102%''
'
EXECUTE sp_executesql #cmd
SET #b = #b + 1
SET #cmd = ''
END

Using variable name to run query on multiple tables

what I am trying to do is run a query multiple times over multiple tables, so what I have here is a table of the table names that cycles through setting #tablename to the name of the table on each iteration that I want to run the query on.
As you can see below #tablename is the name of the table I want to run the queries on but how do i run these queries using #tablename as the table name?
CREATE TABLE [BusinessListings].[dbo].[temptablenames]
(id int,
name nvarchar(50),
)
INSERT INTO [BusinessListings].[dbo].[temptablenames] (id, name)
VALUES
(1,'MongoOrganisationsACT1'),
(2,'MongoOrganisationsNSW1'),
(3,'MongoOrganisationsNT1'),
(4,'MongoOrganisationsQLD1'),
(5,'MongoOrganisationsSA1'),
(6,'MongoOrganisationsTAS1'),
(7,'MongoOrganisationsVIC1'),
(8,'MongoOrganisationsWA1');
DECLARE #tablename sysname,
#id int
SET #id = 1
WHILE (#id < 9)
BEGIN
select #tablename = name from temptablenames where id = #id
select #tablename
select _key_out, sum(quality_score) as sumscore, count(*) as reccount, (sum(quality_score) / count(*)) as ave
into tempga0
from #tablename
group by _key_out
select _key_out, count(*) as reccount
into tempga3
from #tablename
where dedupe_result is null
group by _key_out
having count(*)>1
select a._key_out, max(quality_score) as maxdedupetotalscore
into tempga4
from
#tablename a
join
tempga3 b
on a._key_out = B._key_out
--where isdeleted is null
group by a._key_out
--- keep records
update #tablename
set dedupe_result = 'Keep'
from
#tablename a
join
tempga4 b
on a._key_out = B._key_out
where a.quality_score = b.maxdedupetotalscore
--and isdeleted is null
and dedupe_result is null
SET #id = #id + 1
END
GO
DROP TABLE [BusinessListings].[dbo].[temptablenames]
note: this is only part of the queries that I want run, I just want to figure out how to subsitute the variable in the query as the table name. Also I know this isnt good form but there is a reason I need to do it this way.
updated working code here:
DECLARE #tablename nvarchar(30),
#id int,
#SQLStr nvarchar(1000)
SET #id = 1
WHILE (#id < 9)
BEGIN
select #tablename = name from temptablenames where id = #id
IF OBJECT_ID('tempga0') IS NOT NULL
DROP TABLE tempga0
set #SQLStr = 'select _key_out, sum(quality_score) as sumscore, count(*) as reccount, (sum(quality_score) / count(*)) as ave
into tempga0
from ' + #tablename + ' group by _key_out'
exec(#SQLStr)
SET #id = #id + 1
END
GO
Use the Exec command. Write your query in a variable like and execute it
Declare #SQLStr = 'Select * into X from ' + #tablename
exec(#SQLStr)
You just have to be carefull. I see that you are using into statements. You will have to check that the table does not already exist because you will get an exception. You will need to drop the tables, or a better way would be to do this before you start your loop:
CREATE TABLE tempga0 (
_key_out int,
sumscore numeric(18,9),
reccount int,
ave numeric(18,9))
--rest of the tables to be created here...
Create all the tables, and when you start your While loop add a
WHILE (#id < 9)
BEGIN
TRUNCATE TABLE tempga0
--truncate the rest of the tables
--Do the rest of your stuff here
END
Hope it helps

How to use table variable in a dynamic sql statement?

In my stored procedure I declared two table variables on top of my procedure. Now I am trying to use that table variable within a dynamic sql statement but I get this error at the time of execution of that procedure. I am using Sql Server 2008.
This is how my query looks like,
set #col_name = 'Assoc_Item_'
+ Convert(nvarchar(2), #curr_row1);
set #sqlstat = 'update #RelPro set '
+ #col_name
+ ' = (Select relsku From #TSku Where tid = '
+ Convert(nvarchar(2), #curr_row1) + ') Where RowID = '
+ Convert(nvarchar(2), #curr_row);
Exec(#sqlstat);
And I get the following errors,
Must declare the table variable "#RelPro".
Must declare the table variable "#TSku".
I have tried to take the table outside of the string block of dynamic query but to no avail.
On SQL Server 2008+ it is possible to use Table Valued Parameters to pass in a table variable to a dynamic SQL statement as long as you don't need to update the values in the table itself.
So from the code you posted you could use this approach for #TSku but not for #RelPro
Example syntax below.
CREATE TYPE MyTable AS TABLE
(
Foo int,
Bar int
);
GO
DECLARE #T AS MyTable;
INSERT INTO #T VALUES (1,2), (2,3)
SELECT *,
sys.fn_PhysLocFormatter(%%physloc%%) AS [physloc]
FROM #T
EXEC sp_executesql
N'SELECT *,
sys.fn_PhysLocFormatter(%%physloc%%) AS [physloc]
FROM #T',
N'#T MyTable READONLY',
#T=#T
The physloc column is included just to demonstrate that the table variable referenced in the child scope is definitely the same one as the outer scope rather than a copy.
Your EXEC executes in a different context, therefore it is not aware of any variables that have been declared in your original context. You should be able to use a temp table instead of a table variable as shown in the simple demo below.
create table #t (id int)
declare #value nchar(1)
set #value = N'1'
declare #sql nvarchar(max)
set #sql = N'insert into #t (id) values (' + #value + N')'
exec (#sql)
select * from #t
drop table #t
You don't have to use dynamic SQL
update
R
set
Assoc_Item_1 = CASE WHEN #curr_row = 1 THEN foo.relsku ELSE Assoc_Item_1 END,
Assoc_Item_2 = CASE WHEN #curr_row = 2 THEN foo.relsku ELSE Assoc_Item_2 END,
Assoc_Item_3 = CASE WHEN #curr_row = 3 THEN foo.relsku ELSE Assoc_Item_3 END,
Assoc_Item_4 = CASE WHEN #curr_row = 4 THEN foo.relsku ELSE Assoc_Item_4 END,
Assoc_Item_5 = CASE WHEN #curr_row = 5 THEN foo.relsku ELSE Assoc_Item_5 END,
...
from
(Select relsku From #TSku Where tid = #curr_row1) foo
CROSS JOIN
#RelPro R
Where
R.RowID = #curr_row;
You can't do this because the table variables are out of scope.
You would have to declare the table variable inside the dynamic SQL statement or create temporary tables.
I would suggest you read this excellent article on dynamic SQL.
http://www.sommarskog.se/dynamic_sql.html
Well, I figured out the way and thought to share with the people out there who might run into the same problem.
Let me start with the problem I had been facing,
I had been trying to execute a Dynamic Sql Statement that used two temporary tables I declared at the top of my stored procedure, but because that dynamic sql statment created a new scope, I couldn't use the temporary tables.
Solution:
I simply changed them to Global Temporary Variables and they worked.
Find my stored procedure underneath.
CREATE PROCEDURE RAFCustom_Room_GetRelatedProducts
-- Add the parameters for the stored procedure here
#PRODUCT_SKU nvarchar(15) = Null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..##RelPro', 'U') IS NOT NULL
BEGIN
DROP TABLE ##RelPro
END
Create Table ##RelPro
(
RowID int identity(1,1),
ID int,
Item_Name nvarchar(max),
SKU nvarchar(max),
Vendor nvarchar(max),
Product_Img_180 nvarchar(max),
rpGroup int,
Assoc_Item_1 nvarchar(max),
Assoc_Item_2 nvarchar(max),
Assoc_Item_3 nvarchar(max),
Assoc_Item_4 nvarchar(max),
Assoc_Item_5 nvarchar(max),
Assoc_Item_6 nvarchar(max),
Assoc_Item_7 nvarchar(max),
Assoc_Item_8 nvarchar(max),
Assoc_Item_9 nvarchar(max),
Assoc_Item_10 nvarchar(max)
);
Begin
Insert ##RelPro(ID, Item_Name, SKU, Vendor, Product_Img_180, rpGroup)
Select distinct zp.ProductID, zp.Name, zp.SKU,
(Select m.Name From ZNodeManufacturer m(nolock) Where m.ManufacturerID = zp.ManufacturerID),
'http://s0001.server.com/is/sw11/DG/' +
(Select m.Custom1 From ZNodeManufacturer m(nolock) Where m.ManufacturerID = zp.ManufacturerID) +
'_' + zp.SKU + '_3?$SC_3243$', ep.RoomID
From Product zp(nolock) Inner Join RF_ExtendedProduct ep(nolock) On ep.ProductID = zp.ProductID
Where zp.ActiveInd = 1 And SUBSTRING(zp.SKU, 1, 2) <> 'GC' AND zp.Name <> 'PLATINUM' AND zp.SKU = (Case When #PRODUCT_SKU Is Not Null Then #PRODUCT_SKU Else zp.SKU End)
End
declare #curr_row int = 0,
#tot_rows int= 0,
#sku nvarchar(15) = null;
IF OBJECT_ID('tempdb..##TSku', 'U') IS NOT NULL
BEGIN
DROP TABLE ##TSku
END
Create Table ##TSku (tid int identity(1,1), relsku nvarchar(15));
Select #curr_row = (Select MIN(RowId) From ##RelPro);
Select #tot_rows = (Select MAX(RowId) From ##RelPro);
while #curr_row <= #tot_rows
Begin
select #sku = SKU from ##RelPro where RowID = #curr_row;
truncate table ##TSku;
Insert ##TSku(relsku)
Select distinct top(10) tzp.SKU From Product tzp(nolock) INNER JOIN
[INTRANET].raf_FocusAssociatedItem assoc(nolock) ON assoc.associatedItemID = tzp.SKU
Where (assoc.isActive=1) And (tzp.ActiveInd = 1) AND (assoc.productID = #sku)
declare #curr_row1 int = (Select Min(tid) From ##TSku),
#tot_rows1 int = (Select Max(tid) From ##TSku);
If(#tot_rows1 <> 0)
Begin
While #curr_row1 <= #tot_rows1
Begin
declare #col_name nvarchar(15) = null,
#sqlstat nvarchar(500) = null;
set #col_name = 'Assoc_Item_' + Convert(nvarchar(2), #curr_row1);
set #sqlstat = 'update ##RelPro set ' + #col_name + ' = (Select relsku From ##TSku Where tid = ' + Convert(nvarchar(2), #curr_row1) + ') Where RowID = ' + Convert(nvarchar(2), #curr_row);
Exec(#sqlstat);
set #curr_row1 = #curr_row1 + 1;
End
End
set #curr_row = #curr_row + 1;
End
Select * From ##RelPro;
END
GO
I don't think that is possible (though refer to the update below); as far as I know a table variable only exists within the scope that declared it. You can, however, use a temp table (use the create table syntax and prefix your table name with the # symbol), and that will be accessible within both the scope that creates it and the scope of your dynamic statement.
UPDATE: Refer to Martin Smith's answer for how to use a table-valued parameter to pass a table variable in to a dynamic SQL statement. Also note the limitation mentioned: table-valued parameters are read-only.
Here is an example of using a dynamic T-SQL query and then extracting the results should you have more than one column of returned values (notice the dynamic table name):
DECLARE
#strSQLMain nvarchar(1000),
#recAPD_number_key char(10),
#Census_sub_code varchar(1),
#recAPD_field_name char(100),
#recAPD_table_name char(100),
#NUMBER_KEY varchar(10),
if object_id('[Permits].[dbo].[myTempAPD_Txt]') is not null
DROP TABLE [Permits].[dbo].[myTempAPD_Txt]
CREATE TABLE [Permits].[dbo].[myTempAPD_Txt]
(
[MyCol1] char(10) NULL,
[MyCol2] char(1) NULL,
)
-- an example of what #strSQLMain is : #strSQLMain = SELECT #recAPD_number_key = [NUMBER_KEY], #Census_sub_code=TEXT_029 FROM APD_TXT0 WHERE Number_Key = '01-7212'
SET #strSQLMain = ('INSERT INTO myTempAPD_Txt SELECT [NUMBER_KEY], '+ rtrim(#recAPD_field_name) +' FROM '+ rtrim(#recAPD_table_name) + ' WHERE Number_Key = '''+ rtrim(#Number_Key) +'''')
EXEC (#strSQLMain)
SELECT #recAPD_number_key = MyCol1, #Census_sub_code = MyCol2 from [Permits].[dbo].[myTempAPD_Txt]
DROP TABLE [Permits].[dbo].[myTempAPD_Txt]
Using Temp table solves the problem but I ran into issues using Exec so I went with the following solution of using sp_executesql:
Create TABLE #tempJoin ( Old_ID int, New_ID int);
declare #table_name varchar(128);
declare #strSQL nvarchar(3072);
set #table_name = 'Object';
--build sql sting to execute
set #strSQL='INSERT INTO '+#table_name+' SELECT '+#columns+' FROM #tempJoin CJ
Inner Join '+#table_name+' sourceTbl On CJ.Old_ID = sourceTbl.Object_ID'
**exec sp_executesql #strSQL;**