How to use table variable in a dynamic sql statement? - sql

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;**

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.

Pass a table as parameter in openquery

I've created a datatype and declared a table of this type which I intend to pass as a parameter to an OPENQUERY statement. OPENQUERY returns an error that the object has no columns.
Firstly, the table parameter is defined as follows:
CREATE TYPE LooseTimecardsTableType AS TABLE ([LABORKEY] [float] NULL)
GO
DECLARE #DataTable AS LooseTimecardsTableType
INSERT INTO #DataTable
SELECT DISTINCT WOBase.LABORKEY
FROM Lab_WO_DataWH AS WOBase
left outer JOIN Lab_hrs_DataWH LabHrsWH ON LabHrsWH.WORKORDERLABORKEY = WOBase.LABORKEY
WHERE LabHrsWH.WORKORDERLABORKEY IS NULL AND WOBase.LABORKEY IS NOT NULL AND WOBase.LABORPRICE <> 0
The data table returns a single column of LABORKEY's which I want use to restrict the number of records from the OPENQUERY. This in turn is defined as follows:
DECLARE #SQLString NVARCHAR(500), #TableVariable LooseTimecardsTableType
SET #SQLString = N'SELECT * FROM OPENQUERY(Remoteserver, ''SELECT DISTINCT
DA.USERNAME, DA.WORKORDERLABORKEY, LB.PERFORMEDBY
FROM
REMOTE.WORKORDERDETAILAUDITS DA
JOIN REMOTE.WORKORDERLABORBASE LB ON LB.LABORKEY = DA.WORKORDERLABORKEY
JOIN #TableVariable ON #TableVariable.LABORKEY = LB.LABORKEY
WHERE DA.WORKORDERAUDITCATEGORY = 0'')'
EXECUTE sp_executesql #SQLString, N'#TableVariable LooseTimecardsTableType READONLY', #DataTable
Please assist in getting this to work
Instead of using a table variable you could turn the select statement that populates the table into a view using the where clause and then join the view to the table on the remote server.
The table variable cannot be passed as a parameter to OPENQUERY. As my table had only a single column I was able to convert it to a string and pass the string to OPENQUERY as a parameter. I did this within a procedure that accepts a table variable. After parsing the table to a string, the procedure also runs the OPENQUERY.
CREATE PROCEDURE PRM_LIST ( #TableVariable LooseTimecardsTableType READONLY)
AS
DECLARE
#LBKY_NVAR NVARCHAR(2500),
#POINTER INT,
#SQLString NVARCHAR(max)
SELECT #POINTER = MIN(LABORKEY) FROM #TableVariable
WHILE #POINTER IS NOT NULL
BEGIN
SET #LBKY_NVAR = IIF(#LBKY_NVAR IS NULL,'('+''''+ CONVERT(VARCHAR,#POINTER) + '''',
#LBKY_NVAR + ',' + ''''+ CONVERT(VARCHAR,#POINTER) + '''')
SELECT #POINTER = MIN(LABORKEY) FROM #TableVariable WHERE LABORKEY > #POINTER
END
SET #LBKY_NVAR = #LBKY_NVAR + ')'
SET #LBKY_NVAR = REPLACE(#LBKY_NVAR,'''','''''')
SET #SQLString =
N'SELECT * FROM OPENQUERY(REMOTE, ''SELECT DISTINCT
DA.USERNAME, DA.WORKORDERLABORKEY, LB.PERFORMEDBY
FROM
REMOTE.WORKORDERDETAILAUDITS DA
JOIN REMOTE.WORKORDERLABORBASE LB ON LB.LABORKEY = DA.WORKORDERLABORKEY
WHERE DA.WORKORDERAUDITCATEGORY = 0
AND LB.LABORKEY IN '+CAST(#LBKY_NVAR AS nvarchar(2500))+ N'
'')'
EXEC (#SQLString)
GO
The procedure call is:
EXECUTE PRM_LIST #DATATABLE

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.

Getting a temporary table returned from dynamic SQL in SQL Server 2005, and parsing

So I was requested to make a few things.... (it is Monday morning and for some reason this whole thing is turning out to be really hard for me to explain so I am just going to try and post a lot of my code; sorry) Oh - the table idea has to stay. Anything else can be changed but the idea of this table and the parsed field was not my idea but it is my responsibility to execute and make work.
Edit: Sorry the post is long. I do suggest a few possible solutions throughout but my problem is ultimately how everything is returned in a dynamically defined table/table variable from something (sp,view,function,anything...). Please take a minute to read the whole post...
First, I needed a table:
CREATE TABLE TICKET_INFORMATION (
TICKET_INFO_ID INT IDENTITY(1,1) NOT NULL,
TICKET_TYPE INT,
TARGET_ID INT,
TARGET_NAME VARCHAR(100),
INFORMATION VARCHAR(MAX),
TIME_STAMP DATETIME DEFAULT GETUTCDATE()
)
-- insert this row for testing...
INSERT INTO TICKET_INFORMATION (TICKET_TYPE, TARGET_ID, TARGET_NAME, INFORMATION) VALUES (1,1,'RT_ID','IF_ID,int=1&IF_ID,int=2&OTHER,varchar(10)=val,ue3&OTHER,varchar(10)=val,ue4')
The Information column holds data that needs to be parsed into a table. This is where I am having problems. In the resulting table, Target_Name needs to become a column that holds Target_ID as a value for each row in the resulting table.
The string that needs to be parsed is in this format:
#var_name1,#var_datatype1=#var_value1&#var_name2,#var_datatype2=#var_value2&#var_name3,#var_datatype3=#var_value3
And what I ultimately need as a result (in a table or table variable):
RT_ID IF_ID OTHER
1 1 val,ue3
1 2 val,ue3
1 1 val,ue4
1 2 val,ue4
And I need to be able to join on the result. Initially, I was just going to make this a function that returns a table variable but for some reason I can't figure out how to get it into an actual table variable. Whatever parses the string needs to be able to be used directly in queries so I don't think a stored procedure is really the right thing to be using.
This is the code that parses the Information string... it returns in a temporary table. In the end, this needs to be passed either just the information string or an ID number or something... doesn't matter what. Or somehow it can be in a view with the ticket_information table.
-- create/empty temp table for var_name, var_type and var_value fields
if OBJECT_ID('tempdb..#temp') is not null drop table #temp
create table #temp (row int identity(1,1), var_name varchar(max), var_type varchar(30), var_value varchar(max))
-- just setting stuff up
declare #target_name varchar(max), #target_id varchar(max), #info varchar(max)
set #target_name = (select target_name from ticket_information where ticket_info_id = 1)
set #target_id = (select target_id from ticket_information where ticket_info_id = 1)
set #info = (select information from ticket_information where ticket_info_id = 1)
--print #info
-- some of these variables are re-used later
declare #col_type varchar(20), #query varchar(max), #select as varchar(max)
set #query = 'select ' + #target_id + ' as ' + #target_name + ' into #target; '
set #select = 'select * into ##global_temp from #target'
declare #var_name varchar(100), #var_type varchar(100), #var_value varchar(100)
declare #comma_pos int, #equal_pos int, #amp_pos int
set #comma_pos = 1
set #equal_pos = 1
set #amp_pos = 0
-- while loop to parse the string into a table
while #amp_pos < len(#info) begin
-- get new comma position
set #comma_pos = charindex(',',#info,#amp_pos+1)
-- get new equal position
set #equal_pos = charindex('=',#info,#amp_pos+1)
-- set stuff that is going into the table
set #var_name = substring(#info,#amp_pos+1,#comma_pos-#amp_pos-1)
set #var_type = substring(#info,#comma_pos+1,#equal_pos-#comma_pos-1)
-- get new ampersand position
set #amp_pos = charindex('&',#info,#amp_pos+1)
if #amp_pos=0 or #amp_pos<#equal_pos set #amp_pos = len(#info)+1
-- set last variable for insert into table
set #var_value = substring(#info,#equal_pos+1,#amp_pos-#equal_pos-1)
-- put stuff into the temp table
insert into #temp (var_name, var_type, var_value) values (#var_name, #var_type, #var_value)
-- is this a new field?
if ((select count(*) from #temp where var_name = (#var_name)) = 1) begin
set #query = #query + ' create table #' + #var_name + '_temp (' + #var_name + ' ' + #var_type + '); '
set #select = #select + ', #' + #var_name + '_temp '
end
set #query = #query + ' insert into #' + #var_name + '_temp values (''' + #var_value + '''); '
end
if OBJECT_ID('tempdb..##global_temp') is not null drop table ##global_temp
exec (#query + #select)
--select #query
--select #select
select * from ##global_temp
Okay. So, the result I want and need is now in ##global_temp. How do I put all of that into something that can be returned from a function (or something)? Or can I get something more useful returned from the exec statement? In the end, the results of the parsed string need to be in a table that can be joined on and used... Ideally this would have been a view but I guess it can't with all the processing that needs to be done on that information string.
Ideas?
Thanks!
Edit: Still looking for answers to this. I would love to have a function that returns a table variable but I don't know how to get the results into a table variable. The result is currently in a global temporary table. Would it work if I defined my table variable in the dynamic portion of the code and then it would just magically be there to return? Or can I somehow select into a table variable from my global temp table, without first defining the columns of the table variable? Or can I create the table variable when I execute the dynamic part? The whole problem is because the columns of the end result are dynamic....... so..... I'm not sure how I could clarify the issues I'm having more. If a function that returns a table is a good route to go - could someone please provide me with code or a link as an example for returning a table variable with a dynamic column definition from a function? Plz, thnx.
You could use a table valued function. This would allow you to return the results as a table to be joined to just like you asked for.
CREATE FUNCTION dbo.GET_TICKET_INFORMATION (... some parameters... )
RETURNS #TICKET_INFORMATION TABLE
(
TICKET_INFO_ID INT IDENTITY(1,1) NOT NULL,
TICKET_TYPE INT,
TARGET_ID INT,
TARGET_NAME VARCHAR(100),
INFORMATION VARCHAR(MAX),
TIME_STAMP DATETIME DEFAULT GETUTCDATE()
)
AS
...
I used stored procedure for returning a table with a dynamic column definition
i generated dynamic name for global table:
declare #sp varchar(3)
set #sp = cast( ##spid as varchar(3) )
if object_id ( N'tempdb.dbo.#Periods' ) is not null
drop table #Periods
if object_id ( N'tempdb.dbo.##Result' + #sp ) is not null
execute ( 'drop table ##Result' + #sp )
i have sp for return periods table:
create table #Periods
(
[PERIOD_NUM] int
,[START_DATE] datetime
,[END_DATE] datetime
)
insert into #Periods
exec GET_PERIODS_TABLE_SP #pFromDate, #pToDate, #pPeriodType, 0
some fields in result table are dynamic:
select #PeriodCount = ...
declare #PeriodsScript varchar(max)
set #PeriodsScript = ''
set #i = 1
while #i <= #PeriodCount
begin
set #PeriodsScript = #PeriodsScript + ',PERIOD' + cast (#i as varchar(3))
set #i = #i + 1
end
generated and inserted data into ##Result:
declare #script varchar(max)
set #script = 'create table ##Result' + #sp +
'(ROW_NUM int'+
',BRANCH_ID int' +
',PARAM_NAME varchar(25)' +
#PeriodsScript + ')'
execute ( #script )
execute(
'insert into ##Result' + #sp + '( ROW_NUM, BRANCH_ID, NOM_SIZE_ID, PARAM_NAME )' +
'select ( row_number() over( order by BRANCH_ID, NOM_SIZE_ID ) - 1 ) * 3 + 1' +
' ,BRANCH_ID' +
' ,NOM_SIZE_ID' +
' ,''Min.price''' +
' from ( ' +
' select distinct BRANCH_ID' +
' ,NOM_SIZE_ID' +
' from ##ResultNomSizePrices' + #sp +
' ) as t'
)
and finaly, select from result table:
set #script =
'select distinct gb.TINY_NAME'+
' ,r.GROUP_NAME_1 as group1'+
' ,r.GROUP_NAME_2 as group2'+
' ,r.GROUP_NAME_3 as group3'+
' ,r.PARAM_NAME'+
' ,r.ROW_NUM'+
#PeriodsScript +
' from ##Result' + #sp + ' as r'+
' inner join dbo.GD_BRANCHES as gb'+
' on r.BRANCH_ID = gb.BRANCH_ID'+
' order by gb.TINY_NAME'+
' ,r.GROUP_NAME_1'+
' ,r.GROUP_NAME_2'+
' ,r.GROUP_NAME_3'+
' ,r.ROW_NUM'
execute ( #script )
p.s. sry for my english

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