How to Set a variable using OPENQUERY in SQL Server - sql

I am trying to read data from a table. This table have a list of table name.
for each row of the data set I want to run a couple of queries to pull data and insert it into a temporary table.
Here is What I have done
DECLARE #campName varchar(255);
DECLARE #sqlCommand varchar(1000);
DECLARE #sqlCommandMySQL varchar(1000);
DECLARE #LastRun varchar(60);
DECLARE #OPENQUERY varchar(1000);
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT LTRIM(RTRIM(CallListName)) AS CallListName
FROM [SMSQL1].[RDI_System].[dbo].[Campaigns]
WHERE dialer_campaign = 1 AND i3Server ='I3New' AND ClientID = 111 AND (EndDate IS NULL OR EndDate >= getdate() - 7)
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #campName
WHILE ##FETCH_STATUS = 0
BEGIN
--SET #LinkedServer = 'GUARDIAN';
SET #OPENQUERY = 'SELECT #LastRun = lr FROM OPENQUERY(GUARDIAN,''';
SET #sqlCommandMySQL = 'SELECT IFNULL(MAX(lastRun), DATE_SUB(NOW(), INTERVAL 7 DAY) ) AS lr
FROM guardian_dynamo.runtimes_i3
WHERE CampaignListName = "'+#campName+'" '')';
print #OPENQUERY + #sqlCommandMySQL;
EXEC(#OPENQUERY + #sqlCommandMySQL);
SET #sqlCommand = ' INSERT INTO #finalList(Attemtps, CAMPAIGNNAME, FINISHCODE, CALLDATE, AGENTID, RDINotes, PHONE, MERCHANTAccount)
SELECT ATTEMPTS, CAMPAIGNNAME, FINISHCODE, CALLDATE, AGENTID, RDINotes, PHONE, MERCHANTAccount
FROM [I3_IC4].[dbo].['+ #campName +']
WHERE CALLDATE > '''+#LastRun+''' AND ISNULL(status, ''C'') IN (''U'', ''E'', ''A'', ''F'') ';
EXEC (#sqlCommand);
FETCH NEXT FROM MY_CURSOR INTO #campName
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR;
every time I run this query I get this error
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#LastRun".
I am not sure why since I am declaring this variable on the top as you can see in my code above.
the took the output of print #OPENQUERY + #sqlCommandMySQL; and executed that manually. It worked with no issue and the variable #LastRun will have a datetime value as it should.

You need to use sp_executesql to execute the dynamic query which helps you output the variable(#LastRun)
Declare #OPENQUERY Nvarchar(max), #sqlCommandMySQL Nvarchar(max), #OPENQUERYFINAL Nvarchar(max)
....
SET #OPENQUERY = 'SELECT #LastRun = lr FROM OPENQUERY(GUARDIAN,''';
SET #sqlCommandMySQL = 'SELECT IFNULL(MAX(lastRun), DATE_SUB(NOW(), INTERVAL 7 DAY) ) AS lr
FROM guardian_dynamo.runtimes_i3
WHERE CampaignListName = "'+#campName+'" '')';
--print #OPENQUERY + #sqlCommandMySQL;
SET #OPENQUERYFINAL = #OPENQUERY + #sqlCommandMySQL;
EXEC sp_executesql #OPENQUERYFINAL,
N'#LastRun varchar(10) OUTPUT',
#LastRun output
Demo
DECLARE #str VARCHAR(10),
#sql NVARCHAR(max)
SET #sql= 'select #str=1 '
EXEC Sp_executesql
#sql,
N'#str varchar(10) OUTPUT',
#str output
PRINT #str

Related

Execute dynamic query only to get affected row count

I want to execute a dynamic query to get the affected row count. But SQL Result pane returns me the result after executing it. How to avoid returning the columns. I tried the below way.
DECLARE #Command NVARCHAR(MAX)= 'SELECT * FROM Product WHERE ID = 12'
DECLARE #Count AS INT
EXEC sp_executesql #Command, N'#C INT OUTPUT', #C=#Count OUTPUT
IF (#Count > 0)
BEGIN
EXECUTE (#Command)
END
ELSE
BEGIN
DECLARE #CatalogProduct VARCHAR(MAX) = 'SELECT p.ManufactureCode,p.PartNo,p.Size,p.ID AS ProductID,p.Name ,p.ParentProductID,p.BasePrice FROM Product.Product p WHERE p.ThruDate > GETDATE() '+#Where
EXECUTE (#CatalogProduct)
END
END
I want to avoid returning the null column set from the above attached image.
You can turn off the display, but I think a better approach is to get the count you want directly:
DECLARE #Command NVARCHAR(MAX)= 'SELECT * FROM Product WHERE ID = 12';
DECLARE #count AS INT;
DECLARE #CntCommand NVARCHAR(MAX);
SET #CntCommand = 'SELECT #count = COUNT(*) FROM (' + #Command + ') x)';
EXEC sp_executesql #CntCommand, N'#count INT OUTPUT', #count=#count OUTPUT;
Why not simply?
IF (SELECT COUNT(*) FROM Product = 12) > 0 BEGIN...
I can't see why the COUNT statement needs to be dynamic; there's nothing dynamic about it.
Also, having the SQL '... WHERE p.ThruDate > GETDATE() '+#Where is a terrible idea. If #where is a parameter it'll be wide open to SQL injection.
Try this one. Returns number of rows affected by the last query:
select ##Rowcount
DECLARE #Command NVARCHAR(MAX)= 'SELECT * FROM Product WHERE ID = 12'
DECLARE #CountCommand NVARCHAR(MAX)= 'SELECT #Count=count(1) FROM Product WHERE ID = 12'
DECLARE #Count AS INT
EXEC sp_executesql #CountCommand , N'#Count INT OUTPUT', #Count=#Count OUTPUT
IF (#Count > 0)
BEGIN
EXECUTE (#Command)
END
ELSE
BEGIN
DECLARE #CatalogProduct VARCHAR(MAX) = 'SELECT p.ManufactureCode,p.PartNo,p.Size,p.ID AS ProductID,p.Name ,p.ParentProductID,p.BasePrice FROM Product.Product p WHERE p.ThruDate > GETDATE() '+#Where
EXECUTE (#CatalogProduct)
END
END

Compare two table using dynamic database

I have 2 table (same name) on 2 different database
I want compare these table, this is my procedure
ALTER PROCEDURE COUNTCOLUMN
#TABLENAME NVARCHAR(MAX),
#DATABASENAME1 NVARCHAR(MAX),
#DATABASENAME2 NVARCHAR(MAX)
AS
BEGIN
DECLARE #COLNAME NVARCHAR(MAX)
DECLARE #ROWCOUNT INT
DECLARE #ROWCOUNT2 INT
DECLARE #NUMB1 INT
DECLARE #NUMB2 INT
DECLARE #SQLQUERY NVARCHAR(MAX)
DECLARE #SQLQUERY2 NVARCHAR(MAX)
DECLARE #SQLQUERY3 NVARCHAR(MAX)
DECLARE #SQLQUERY4 NVARCHAR(MAX)
DECLARE #SQLQUERY5 NVARCHAR(MAX)
DECLARE #SQLQUERY6 NVARCHAR(MAX)
SET #SQLQUERY = 'SELECT #NUMBER = COUNT(*) FROM information_schema.columns WHERE table_name = '+N'#TESTTB'+''
SET #SQLQUERY2 = 'USE ' + #DATABASENAME1
SET #SQLQUERY3 = 'USE ' + #DATABASENAME2
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY, N'#TESTTB NVARCHAR(MAX), #NUMBER INT OUTPUT',#TESTTB = #TABLENAME ,#NUMBER = #NUMB1 OUTPUT
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY, N'#TESTTB NVARCHAR(MAX), #NUMBER INT OUTPUT',#TESTTB = #TABLENAME ,#NUMBER = #NUMB2 OUTPUT
IF(#NUMB1 <= #NUMB2)
BEGIN
DECLARE #NUMB INT
SET #NUMB = 1
WHILE (#NUMB <= #NUMB1)
BEGIN
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEMP1'))
BEGIN
DROP TABLE TEMP1
END
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEMP2'))
BEGIN
DROP TABLE TEMP2
END
ELSE
BEGIN
SET #SQLQUERY4 = 'SELECT #COL_NAME = COL_NAME(OBJECT_ID(#TABLENAME), #NUMB)'
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY4, N'#TABLENAME NVARCHAR(MAX),#NUMB INT, #COL_NAME NVARCHAR(MAX) OUTPUT', #TABLENAME = #TABLENAME,#NUMB=#NUMB, #COL_NAME = #COLNAME OUTPUT
SET #SQLQUERY5 = 'SELECT '+ #COLNAME + ' INTO TEST_DB..TEMP1 FROM ' + #TABLENAME
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY5
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY4, N'#TABLENAME NVARCHAR(MAX),#NUMB INT, #COL_NAME NVARCHAR(MAX) OUTPUT', #TABLENAME = #TABLENAME,#NUMB=#NUMB, #COL_NAME = #COLNAME OUTPUT
SET #SQLQUERY6 = 'SELECT ' + #COLNAME +' INTO TEST_DB..TEMP2 FROM '+ #TABLENAME
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY6
SELECT #ROWCOUNT = COUNT(*) FROM TEMP1
SELECT #ROWCOUNT2 = COUNT(*) FROM TEMP2
IF(#ROWCOUNT != #ROWCOUNT2)
BEGIN
PRINT N'NUMBER OF ROWS ARE NOT EQUAL';
BREAK
END
ELSE IF (#NUMB > #NUMB1)
BREAK
ELSE
BEGIN
SELECT * FROM TEST_DB..TEMP1
EXCEPT
SELECT * FROM TEST_DB..TEMP2
SET #NUMB = #NUMB + 1;
CONTINUE
END
END
END
END
END
It should be like this TEMP1 <--TEST_DB..TESTTABLE, TEMP2 <--TEST_DB2..TESTTABLE
But TEMP1 , TEMP2 always have the same values as TESTTABLE from the first database (TEST_DB )
It seem like I can't switch between two databases in procedure so I try to run it separate but still not work

Select Top 1 field and assign to local variable where database name is dynamic

When executing below sql server i am unable to pass #AKM parameter in the code how ever i am able to reproduce the same which doing in simple query's. However i am able to pass variable for id as it is an int type but #akm is in charecter
CREATE PROCEDURE [dbo].[sp_akm_test]
#AKM NVARCHAR(128),
#Anlsid Int
AS
DECLARE #Sql1 NVARCHAR(MAX);
DECLARE #type NVARCHAR(MAX);
BEGIN
SELECT TOP 1 #type= sample_type FROM ' + #RDM +'.dbo.analysis where ID= '#id
PRINT(#type)
IF (#type = 8017)
BEGIN
SET #Sql1 = N'select sample_n FROM'+ QUOTENAME(#AKM) +'..sample'
END
ELSE
Begin
SET #Sql1 = N'select sampleid FROM'+ QUOTENAME(#AKM) +'..sample'
END
EXECUTE sp_executesql #Sql1
END
I guess you need something like this
declare #RDM sysname = ???? -- pass the db name, missing in your code
declare #sql nvarchar(4000)= '',#type int
set #sql = '
SELECT TOP 1 #type= sample_type FROM ' + quotename(#RDM) +'.dbo.rdm_analysis where ID=110'
exec sp_executesql #sql,N'#type int output',#type = #type output
SET #Sql1 = '
select case when #type = 8017 then sample_n else sampleid end
FROM'+ QUOTENAME(#AKM) +'..sample'
exec sp_executesql #sql1,N'#type int',#type

Why I can't set the value [duplicate]

I am trying to execute this query:
declare #tablename varchar(50)
set #tablename = 'test'
select * from #tablename
This produces the following error:
Msg 1087, Level 16, State 1, Line 5
Must declare the table variable "#tablename".
What's the right way to have the table name populated dynamically?
For static queries, like the one in your question, table names and column names need to be static.
For dynamic queries, you should generate the full SQL dynamically, and use sp_executesql to execute it.
Here is an example of a script used to compare data between the same tables of different databases:
Static query:
SELECT * FROM [DB_ONE].[dbo].[ACTY]
EXCEPT
SELECT * FROM [DB_TWO].[dbo].[ACTY]
Since I want to easily change the name of table and schema, I have created this dynamic query:
declare #schema sysname;
declare #table sysname;
declare #query nvarchar(max);
set #schema = 'dbo'
set #table = 'ACTY'
set #query = '
SELECT * FROM [DB_ONE].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table) + '
EXCEPT
SELECT * FROM [DB_TWO].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table);
EXEC sp_executesql #query
Since dynamic queries have many details that need to be considered and they are hard to maintain, I recommend that you read: The curse and blessings of dynamic SQL
Change your last statement to this:
EXEC('SELECT * FROM ' + #tablename)
This is how I do mine in a stored procedure. The first block will declare the variable, and set the table name based on the current year and month name, in this case TEST_2012OCTOBER. I then check if it exists in the database already, and remove if it does. Then the next block will use a SELECT INTO statement to create the table and populate it with records from another table with parameters.
--DECLARE TABLE NAME VARIABLE DYNAMICALLY
DECLARE #table_name varchar(max)
SET #table_name =
(SELECT 'TEST_'
+ DATENAME(YEAR,GETDATE())
+ UPPER(DATENAME(MONTH,GETDATE())) )
--DROP THE TABLE IF IT ALREADY EXISTS
IF EXISTS(SELECT name
FROM sysobjects
WHERE name = #table_name AND xtype = 'U')
BEGIN
EXEC('drop table ' + #table_name)
END
--CREATES TABLE FROM DYNAMIC VARIABLE AND INSERTS ROWS FROM ANOTHER TABLE
EXEC('SELECT * INTO ' + #table_name + ' FROM dbo.MASTER WHERE STATUS_CD = ''A''')
Use:
CREATE PROCEDURE [dbo].[GetByName]
#TableName NVARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sSQL nvarchar(500);
SELECT #sSQL = N'SELECT * FROM' + QUOTENAME(#TableName);
EXEC sp_executesql #sSQL
END
You can't use a table name for a variable. You'd have to do this instead:
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = 'SELECT * from yourtable'
EXEC (#sqlCommand)
You'll need to generate the SQL content dynamically:
declare #tablename varchar(50)
set #tablename = 'test'
declare #sql varchar(500)
set #sql = 'select * from ' + #tablename
exec (#sql)
Use sp_executesql to execute any SQL, e.g.
DECLARE #tbl sysname,
#sql nvarchar(4000),
#params nvarchar(4000),
#count int
DECLARE tblcur CURSOR STATIC LOCAL FOR
SELECT object_name(id) FROM syscolumns WHERE name = 'LastUpdated'
ORDER BY 1
OPEN tblcur
WHILE 1 = 1
BEGIN
FETCH tblcur INTO #tbl
IF ##fetch_status <> 0
BREAK
SELECT #sql =
N' SELECT #cnt = COUNT(*) FROM dbo.' + quotename(#tbl) +
N' WHERE LastUpdated BETWEEN #fromdate AND ' +
N' coalesce(#todate, ''99991231'')'
SELECT #params = N'#fromdate datetime, ' +
N'#todate datetime = NULL, ' +
N'#cnt int OUTPUT'
EXEC sp_executesql #sql, #params, '20060101', #cnt = #count OUTPUT
PRINT #tbl + ': ' + convert(varchar(10), #count) + ' modified rows.'
END
DEALLOCATE tblcur
You need to use the SQL Server dynamic SQL:
DECLARE #table NVARCHAR(128),
#sql NVARCHAR(MAX);
SET #table = N'tableName';
SET #sql = N'SELECT * FROM ' + #table;
Use EXEC to execute any SQL:
EXEC (#sql)
Use EXEC sp_executesql to execute any SQL:
EXEC sp_executesql #sql;
Use EXECUTE sp_executesql to execute any SQL:
EXECUTE sp_executesql #sql
Declare #tablename varchar(50)
set #tablename = 'Your table Name'
EXEC('select * from ' + #tablename)
Also, you can use this...
DECLARE #SeqID varchar(150);
DECLARE #TableName varchar(150);
SET #TableName = (Select TableName from Table);
SET #SeqID = 'SELECT NEXT VALUE FOR ' + #TableName + '_Data'
exec (#SeqID)
Declare #fs_e int, #C_Tables CURSOR, #Table varchar(50)
SET #C_Tables = CURSOR FOR
select name from sysobjects where OBJECTPROPERTY(id, N'IsUserTable') = 1 AND name like 'TR_%'
OPEN #C_Tables
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
WHILE ( #fs_e <> -1)
BEGIN
exec('Select * from ' + #Table)
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
END

The number of variables declared in the INTO list must match that of selected columns one select one into

This is a pretty straightforward error, but I can't figure out why I am getting it. I have one column selected in my declare (TABLE_NAME) and am fetching into one variable (#cTableName). What gives?
CREATE PROCEDURE [dbo].[updateNumbers_ArchiveDB]
(
#accountNumber varchar(50),
#padding varchar(50),
#proc_dateStart datetime,
#proc_dateEnd datetime
)
AS
DECLARE #cTableName varchar(50)
DECLARE CursorYearlyTables CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME like 'Archive[12]%' and len(TABLE_NAME) = 14
ORDER BY TABLE_NAME;
-- =============================================
-- Open the cursor and iterate over the monthlies
-- =============================================
OPEN CursorYearlyTables
fetch next from CursorYearlyTables into #cTableName
while (##fetch_status <> -1)
BEGIN
SET NOCOUNT ON;
declare #q varchar(1000);
set #q = 'Update' + #cTableName +
'SET LogicalAccount = #padding + #accountNumber' +
'WHERE ProcessDate BETWEEN CAST(#proc_dateStart AS DATE) AND CAST(#proc_dateEnd AS DATE)'
exec(#q)
fetch next from CursorYearlyTables into #cTableName
END
close CursorYearlyTables;
DEALLOCATE CursorYearlyTables;
Could you try it with these lines in the cursor
declare #q nvarchar(max);
set #q = 'Update ' + #cTableName +
'SET LogicalAccount = '+#padding + #accountNumber +
'WHERE ProcessDate BETWEEN CAST('''+CONVERT(VARCHAR(20),#proc_dateStart)+''' AS DATE) AND CAST('''+CONVERT(VARCHAR(20),#proc_dateEnd)+''' AS DATE)'
exec sp_executesql #q
to account for SQL_Injection DavidG commented:
declare
#q nvarchar(max) = 'update '+#cTableName+' SET LogicalAccount = #a where ProcessDate BETWEEN CAST(#b AS DATE) AND CAST(#c AS DATE))',
#param1 nvarchar(100) = #padding+ #accountNumber,
#ParamDefinition nvarchar(500) = N'#a varchar(100), #b datetime, #c datetime'
exec sp_executesql #q, #ParamDefinition,#a = #param1,#b = #proc_dateStart, #cTableName = #proc_dateEnd
Please use CONVERT function
CAST('''+CONVERT(VARCHAR(108),#proc_dateStart)+''' AS DATE) AND CAST('''+CONVERT(VARCHAR(108),#proc_dateEnd)+''' AS DATE)'