Dynamic declaration in tsql - sql

is it possible to do dynamic declarations?
I will explain: I have a table COLUMNAMES:
ID|Name
1|Country
2|City
3|District
4|Neighbourhood
For each record in that table I would like to do something like:
declare #i int = 1
declare #number int
set #number = (SELECT count(*) FROM COLUMNNAMES)
While #i <= #number
BEGIN
Execute ('Declare column' + #i +'varchar(25)')
Execute ('set column' + #i +' = (Select NAME from COLUMNAMES where id = ' + #i)
set #i = #i + 1
END
The idea is that I get a list of variables (strings) that I can use to create SELECT statements with dynamic table-aliases:
Execute ('Select SOMECOLUMN as ' + #columname + #i +', ANOTHERCOLUMN as ' + #columname + #i +', ATHIRDCOLUMN as ' + #columname + #i + ' FROM SOMETABLE')
Can this be done? If so, how?

Each Execute function as a different session.
So, in order to declare the variable, all the code must be in one Execute function.

No you can't declare variables like this, but you can use a temporary table with the data filled in.
Here is some help, but it is not a whole solution, just an idea what you can do instead of the not working declaration:
Create Table #ColumnNames(
NAME varchar(64)
)
While #i <= #number
BEGIN
INSERT INTO #ColumNames
Select NAME from COLUMNAMES where id = #i
set #i = #i + 1
END
DECLARE #Columns varchar(max)
SET #Columns = ''
SElECT #Columns = #Columns + NAME + ', '
FROM #ColumNames

This is not complete solution but a direction . you need to get the value 'SomeColumn' dynamically someway ,likely the way you are getting the aliases in the below solution.
declare #i int = 1
declare #number INT
DECLARE #ColName VARCHAR(25)
DECLARE #SQL VARCHAR(4000)=''
DECLARE #ColumnsWithAlias VARCHAR(4000) = ''
set #number = (SELECT count(*) FROM COLUMNNAMES)
While #i <= #number
BEGIN
Select #ColName= NAME from COLUMNAMES where id = #i)
SET #ColumnsWithAlias =#ColumnsWithAlias + 'SomeColumn'+ ' AS '+ #ColName + ' , '
set #i = #i + 1
END
SET #SQL= 'SELECT '#ColumnsWithAlias+' FROM TableName'
EXECUTE(#SQL)

Related

Getting OUTPUT from Nested Dynamic SQL with a Remote Server

I am trying to execute some logic on a remote server using dynamic SQL, but when I set the dynamic SQL and try to call it nothing happens.. If I call the execution without making the execution dynamic it works without issue.
Not sure if what I am trying to do is allowed when passing OUTPUT values but figured I would see if anyone has any thoughts
DECLARE #sqlserver NVARCHAR(MAX) = ''
DECLARE #DBName NVARCHAR(MAX) = ''
DECLARE #parms NVARCHAR(MAX) = N'#output INT OUT', #output INT, #sql NVARCHAR(MAX) = '
;WITH DuplicateStatusCodes AS (
SELECT CodeTypeID FROM EDDSDBO.Code WHERE Name = ''Master'' OR Name = ''Unique'' OR Name = ''Duplicate''
)
SELECT ROW_NUMBER() OVER (ORDER BY CodeTypeID) RN, CodeTypeID INTO #temp
FROM DuplicateStatusCodes
GROUP BY CodeTypeID
HAVING COUNT(CodeTypeID) = 3
DECLARE #count INT = (SELECT COUNT(1) FROM #temp)
DECLARE #ctr INT = 1;
IF #count > 0
BEGIN
WHILE #ctr <= #count BEGIN
DECLARE #parms NVARCHAR(MAX) = N''#inneroutput INT OUT'';
DECLARE #inneroutput INT
DECLARE #codeartifact INT = (SELECT CodeTypeID FROM #temp WHERE RN <= 1 * #ctr and RN > 1 * (#ctr - 1))
DECLARE #duplicatestatuscheck NVARCHAR(MAX) = ''SELECT #inneroutput = COUNT(1) FROM eddsdbo.codeartifact_''+CAST(#codeartifact AS VARCHAR)+''''
EXEC sys.sp_executesql #duplicatestatuscheck, #parms, #inneroutput OUT
SELECT #output = #inneroutput
IF #inneroutput > 0
BREAK;
SET #ctr = #ctr + 1;
END
END
ELSE
BEGIN
SELECT #output = ''0''
END
'
DECLARE #linksql NVARCHAR(MAX) = '
DECLARE #sql NVARCHAR(MAX)
DECLARE #parms NVARCHAR(MAX) = N''#output INT OUT''
EXEC '+QUOTENAME(#sqlserver)+'.'+QUOTENAME(#DBName)+'.sys.sp_executesql #sql, #parms, #output = #output OUT'
--This does not work
EXEC sp_executesql #linksql, #parms, #output = #output
PRINT #output
--This works fine
EXEC [RemoteServerName].[DatabaseName].sys.sp_executesql #sql, #parms, #output = #output OUT ```
Appreciate any help
you would need to use OPENROWSET. Something like this:
DECLARE #ServerName VARCHAR(255) = '...'
,#DatabaseName VARCHAR(255) = '...'
,#Pwd VARCHAR(255) = '...'
,#UID VARCHAR(255) = '...'
,#Name VARCHAR(255) = 'Master'
DECLARE #connect varchar(max) = '''Server=' + #ServerName + ';database=' + #DatabaseName + ';Uid=' + #UID + ';Pwd=' + #Pwd + ';'''
declare #sqlstring varchar(max) = 'SET ANSI_NULLS OFF; SET ANSI_WARNINGS OFF;
SELECT CodeTypeID FROM EDDSDBO.Code WHERE Name = ''''' + #Name + ''''' '
declare #TheExecutableStr varchar(max) = '
SELECT *
FROM OPENROWSET(''SQLNCLI'', ' + #connect + ',
''' + #sqlstring + ''')'
SELECT #TheExecutableStr
--EXEC(#TheExecutableStr)
Please note you have to count single quote very diligently, hence SELECT #TheExecutableStr. It is for debugging purposes. You can view the query to be executed and actually copy/paste and execute before uncommenting last statement.
Hope it helps.
just noticed, you have sys.sp_executesql within sys.sp_executesql which might be problematic. you might need to find a way to refactor the code.

Incrementing a parameter of type INT in dynamic SQL

I'm trying to increment a parameter set within my script by 1 every loop of my while.
This is an example of what I'm doing within my script:
DECLARE #I AS INT;
SET #I = 0;
DECLARE #SQL NVARCHAR(MAX)
SET #SQL =
'WHILE '+ Convert(Varchar, #I) +' < (SELECT statement here...)
BEGIN
SET '+ Convert(Varchar, #I) +' = '+ Convert(Varchar, (#I + 1))'
END'
There is a lot more to this script but this is the relevant part. I understand that '+ Convert(Varchar, #I) +' is just going to concatenate the value of #I to the string, but could anyone offer any advice in how I can make it so that the value of #I is incremented by 1.
Currently when executing the sql, the set command will end up like the following:
SET 0 = 0 + 1
where as I need it to change the actual variables value for the next loop.
Is this even possible?
You can use the '#I' as a variable in your query:
I'm trying to increment a parameter set within my script by 1 every loop of my while.
This is an example of what I'm doing within my script:
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = '
DECLARE #I AS INT;
SET #I = 0;
WHILE #I +' < (SELECT statement here...) +
'
BEGIN
SET #I = #I + 1
END'

How to use a variable in SQL statement From Line

I am trying to create a procedure that allows for a variable to be used as a table name.
Example:
Select Temp1, Temp2
From #var
The #var has the correct table name but changes with each iteration through the while loop.
-- Above sets up all Variables
WHILE #Count >= #I
BEGIN
SET #ObjectID = (SELECT ObjectID FROM #TmpTable WHERE ID = #I)
SET #TableName = (SELECT TableName FROM #TmpTable WHERE ID = #I)
IF #I = 8
BEGIN
SELECT *
INTO ##TMPTable
FROM #TableName
-- Additional Processing
END
END
DECLARE
#SQLString varchar(max),
#TempTableName varchar(100)
SET #TempTableName = '##TMPTable' + cast( ##spid AS varchar(5))
WHILE #Count >= #I
BEGIN
SET #ObjectID = (SELECT ObjectID FROM #TmpTable WHERE ID = #I)
SET #TableName = (SELECT TableName FROM #TmpTable WHERE ID = #I)
IF #I = 8
BEGIN
set #SQLString = '
SELECT *
INTO ' + #TempTableName + '
FROM ' + #TableName
EXECUTE ( #SQLString )
EXECUTE ( 'SELECT * from ' + #TempTableName)
-- Additional Processing
END
END

How to avoid the cursor and what is the other way?

I am using the cursor in my stored procedure; I am hoping to remove the cursor from my SP. Please help me come up with a solution for how to avoid the cursor statement to normal update statement with dynamic.
Example Below:
Update Tablename set columnname(variable) = value from table A join Table B on A.condition = B.Condition where name = 'Test'(variable) and age = 18(variable)
Update Tablename set columnname(variable) = value from table A join Table B on A.condition = B.Condition where name = 'kumar'(variable) and age = 19(variable)
Update Tablename set columnname(variable) = value from table A join Table B on A.condition = B.Condition where name = 'babu'(variable) and age = 30(variable)
This is how my cursor will work. 300 Combination dynamically pick the data from table and update into the main table
I am trying to take out the cursor, and update statement should work similar to this, instead of writing 300 update statements, I want to write one update where all the 300 combinations should execute.
Below is my code which needs this solution:
BEGIN
DECLARE #Type VARCHAR(100)
DECLARE #TargetColumn VARCHAR(100)
DECLARE #SourceColumn VARCHAR(100)
DECLARE #SQL varchar(max)
DECLARE a_cursor CURSOR STATIC
FOR
SELECT [Type],[SourceColumn],[TargetColumn] FROM ref.tblEdsMap
GROUP BY [Type],[SourceColumn],[TargetColumn]
OPEN a_cursor
FETCH NEXT FROM a_cursor INTO #Type,#SourceColumn,#TargetColumn
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'UPDATE GCT SET GCT.' + #TargetColumn + ' = map.[TargetValue]
from EdsMap map
JOIN Table GCT
ON GCT.' + #SourceColumn + ' = map.[SourceValue]
where map.[Type]=''' + #Type + ''' and map.SourceColumn=''' + #SourceColumn+ ''''
Exec (#SQL)
PRINT #SQL
FETCH NEXT FROM a_cursor INTO #Type,#SourceColumn,#TargetColumn
END
CLOSE a_cursor
DEALLOCATE a_cursor
END
Rather than use an explicit cursor or a cursor cleverly disguised as a while loop, I prefer row concatenation operations for this type of problem.
DECLARE #cmd NVARCHAR(MAX) = N'';
SELECT #cmd += N'
UPDATE GCT
SET GCT.' + QUOTENAME(TargetColumn) + ' = map.TargetValue
FROM dbo.EdsMap AS map
INNER JOIN dbo.Table AS GCT
ON GCT.' + QUOTENAME(SourceColumn) + ' = map.SourceValue
WHERE map.[Type] = ''' + [Type] + '''
AND map.SourceColumn = ''' + [SourceColumn]+ ''';'
FROM ref.tblEdsMap
GROUP BY [Type], SourceColumn, TargetColumn;
EXEC sp_executesql #sql;
When I've done these in the past, I usually make up a transaction to encompass every update that's needed. Something like this:
CREATE TABLE #targets ([Type] VARCHAR(255),[SourceColumn] VARCHAR(255),[TargetColumn] VARCHAR(255));
INSERT INTO #targets
( [Type], [SourceColumn], [TargetColumn] )
SELECT [Type],[SourceColumn],[TargetColumn] FROM ref.tblEdsMap
GROUP BY [Type],[SourceColumn],[TargetColumn];
DECLARE #sql VARCHAR(MAX);
SET #sql = 'BEGIN TRAN' + CHAR(10) + CHAR(13);
SELECT #sql = #sql +
'UPDATE GCT SET GCT.' + [TargetColumn] + ' = map.[TargetValue]
from EdsMap map
JOIN Table GCT
ON GCT.' + [SourceColumn] + ' = map.[SourceValue]
where map.[Type]=''' + [Type] + ''' and map.SourceColumn=''' + [SourceColumn]+ ''';' + CHAR(10) + CHAR(13)
FROM #targets
SELECT #sql = #sql + 'COMMIT TRAN'
PRINT #sql
Exec (#SQL)
The update statements are still the same, i.e., you get one update per combination. But now you're running as one transaction batch. You could potentially be fancier with the dynamic SQL, so that you had just one update statement, but in my experience, it's too easy to get bad updates that way.
Doing it this way may not be any faster than a cursor. You'd have to test to be sure. With the examples where I've used this approach, it has generally been a faster approach.
Try using a table variable along with a WHILE loop instead, like so:
BEGIN
DECLARE #Type VARCHAR(100)
DECLARE #TargetColumn VARCHAR(100)
DECLARE #SourceColumn VARCHAR(100)
DECLARE #SQL varchar(max)
DECLARE #SomeTable TABLE
(
ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
Type varchar(100),
SourceColumn varchar(100),
TargetColumn varchar(100)
)
DECLARE #Count int, #Max int
INSERT INTO #SomeTable (Type, SourceColumn, TargetColumn)
SELECT [Type],[SourceColumn],[TargetColumn]
FROM ref.tblEdsMap
GROUP BY [Type],[SourceColumn],[TargetColumn]
SELECT #Count = 1, #Max = COUNT(ID)
FROM #SomeTable
WHILE #Count <= #Max
BEGIN
SELECT
#Type = Type,
#SourceColumn = SourceColumn,
#TargetColumn = TargetColumn
FROM #SomeTable
WHERE ID = #Count
-- Your code
SET #SQL = 'UPDATE GCT SET GCT.' + #TargetColumn + ' = map.[TargetValue]
from EdsMap map
JOIN Table GCT
ON GCT.' + #SourceColumn + ' = map.[SourceValue]
where map.[Type]=''' + #Type + ''' and map.SourceColumn=''' + #SourceColumn+ ''''
Exec (#SQL)
PRINT #SQL
SET #Count = #Count + 1
END -- while
END

adding columns to an existing table

I need to add around 600 columns to an existing table for testing purpose
declare #counter int
set #counter = 1
while #counter < 601
begin
ALTER TABLE Info ADD column+#counter varchar(max);
set #counter = #counter + 1
end
The column name should looks like column1,column2....column600
As this is purely for testing a quick and dirty way is to do this:
declare #counter int
set #counter = 1
declare #sql varchar(2000)
while #counter < 601
begin
Set #Sql = 'ALTER TABLE Info ADD column'+convert(varchar(10),#counter)+ ' varchar(max)'
Exec (#Sql)
set #counter = #counter + 1
end
Although I have to wonder why you would want 600 columns in a table
Use dynamic sql execution-
SET #str = 'ALTER TABLE Info ADD column' + CAST(#counter as varchar(5)) + ' varchar(max)';
EXEC ( #str );
GO
use Exec function:
Declare #SQL VarChar(1000)
Declare #vary varchar(100)
Declare #final varchar(1000)
SELECT #SQL = 'ALTER TABLE '
SELECT #SQL = #SQL + #TableName
select #vary = 'add column ' + id
select #final = #sql + #vary
Exec ( #final)
GO