T-SQL cursor for linked server INSERT SELECT - sql-server

I'm trying to get this cursor loop to work, so I could copy data from linked server to another server. However it seems that the cursor is in a loop and does nothing. What am I doing wrong?
/* For testing purposes I'm fetching data from
1 company only. The result should be one row.*/
DECLARE #tmp_key VARCHAR(14)
DECLARE #db cursor
DECLARE #sql NVARCHAR(MAX)
SET #db = CURSOR FOR
SELECT [CompanyId] FROM [Test].[dbo].[Company] WHERE [CompanyId] = '0001'
SET #sql = N'INSERT INTO [Stagingarea].[dbo].[Cominfo]
SELECT
convert(nvarchar(100),[Nro])
,convert(nvarchar(100),'+#tmp_key+' )
FROM [Linked_server_name].TK'+#tmp_key+'.[dbo].[cominfo]
where [rule1] <> 0 and acc = 1777';
OPEN #db
FETCH NEXT FROM #db INTO #tmp_key
while (##fetch_status = 0)
begin
EXEC sp_sqlexec #sql
end;
CLOSE #db
DEALLOCATE #db

You should put the dynamic SQL inside the WHILE loop. Make sure to call FETCH NEXT inside to avoid infinite loop.
DECLARE #tmp_key VARCHAR(14)
DECLARE #db cursor
DECLARE #sql NVARCHAR(MAX)
SET #db = CURSOR FOR
SELECT [CompanyId] FROM [Test].[dbo].[Company] WHERE [CompanyId] = '0001'
OPEN #db
FETCH NEXT FROM #db INTO #tmp_key
WHILE(##FETCH_STATUS = 0) BEGIN
SET #sql = N'INSERT INTO [Stagingarea].[dbo].[Cominfo]
SELECT
CONVERT(NVARCHAR(100), [Nro])
,CONVERT(NVARCHAR(100),' + #tmp_key +')
FROM [Linked_server_name].TK' + #tmp_key + '.[dbo].[cominfo]
WHERE [rule1] <> 0 AND acc = 1777';
EXEC sp_sqlexec #sql
FETCH NEXT FROM #db INTO #tmp_key
END;
CLOSE #db
DEALLOCATE #db

Related

Looping through the cursor in T-SQL

I am creating a procedure in T-SQL:
CREATE PROCEDURE usp_QC1
AS
BEGIN
DECLARE #tablename varchar(100);
DECLARE #sfilename varchar(100);
DECLARE #rread varchar(100);
DECLARE #rwrite varchar(100);
DECLARE #sserver varchar(100);
DECLARE #dbname varchar(100);
DECLARE cur_tracking CURSOR FOR
SELECT
[TableName],
sfilename,
[RecordsRead],
[RecordsWritten],
[SQLServer],
[SQLDatabase]
FROM
[ADataTracker].[dbo].[TrackerLoadLog]
WHERE
LOWER(datagroup) LIKE '%Oracle RMS%'
AND SUBSTRING(sfilename, 1, 10) = '2022-03-16'
OPEN cur_tracking;
FETCH NEXT FROM cur_tracking INTO #tablename, #sfilename, #rread, #rwrite, #sserver, #dbname;
WHILE ##Fetch_status = 0
BEGIN
SELECT
sfilename, COUNT(*)
FROM
#sserver + '.dbo.' + #dbname
WHERE
sfilename = #sfilename
GROUP BY
sfilename
FETCH NEXT FROM cur_tracking INTO #tablename, #sfilename, #rread, #rwrite, #sserver, #dbname;
END;
CLOSE cur_tracking;
DEALLOCATE cur_tracking;
END
The cursor cur_tracking is holding the data (just showing 1 record)
---------------------------------------------------------------------------------------------------------------------------
TableName |sfilename | RecordsRead |RecordsWritten |SQLServer |SQLDatabase |
History |2022-03-16\20220316032322.dat| 5819560 |5819560 |a.stg.sql.ccaintranet.com |DataRms_Back |
So the cursor is having all the information of server, Databasename, tablename. I want to go to those each server.db.table and retrieve the information using these cursor elements:
SELECT sfilename, COUNT(*)
FROM #sserver + '.dbo.' + #dbname
WHERE sfilename = #sfilename
GROUP BY sfilename
But I get an error:
invalid column name sfilename
must declare the table variable #sserver
This is never going to work:
select sfilename,count(*) from #sserver+'.dbo.'+#dbname
You can't parameterize entity names like this (also database comes before schema, and you missed #tablename entirely).
Instead you can do:
DECLARE #sql nvarchar(max), #exec nvarchar(1000);
...
-- then inside the cursor:
SET #sql = N'select sfilename = #sfilename,count(*)
FROM dbo.' + QUOTENAME(#tablename)
+ N' WHERE sfilename = #sfilename;';
SET #exec = QUOTENAME(#dbname) + N'.sys.sp_executesql';
EXEC #exec #sql, N'#sfilename varchar(100)', #sfilename;
Note that all of these need to be nvarchar(128), and you can use the same DECLARE for multiple variables to lighten eye strain:
DECLARE #tablename nvarchar(128),
#sfilename nvarchar(128),
#dbname nvarchar(128);
Please review this collection of links.

Pass Variable in SELECT FROM Loop

I'm looking to select from multiple tables (MainTbl) but it will be based on the result set (StateTbl) of which tables would be pulled.
MainTables dbo.TABLE_MO, dbo.TABLE_CA, dbo.TABLE_AL, dbo.TABLE_MI
Only looking to pull based on resultset StateTbl MO, CA, WA
Declare #Loop_Count int = 0
DECLARE #State varchar(2)
DECLARE #SQL varchar(max)
DECLARE db_cursor CURSOR FOR SELECT State FROM StateTbl
OPEN db_cursor
FETCH db_cursor INTO #State
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #SQL =
'
dbo.TABLE_'+ #State +'
'
EXEC(#SQL)
SET #Loop_Count = #Loop_Count + 1
FETCH db_cursor INTO #SQL
END
CLOSE db_cursor
DEALLOCATE db_cursor
Instead of a loop you can leverage dynamic and the StateTbl to build your dynamic sql. Something like this.
declare #SQL nvarchar(max) = ''
select 'select * from TABLE_' + [State] + ' UNION ALL '
from StateTbl
select #SQL = left(#SQL, len(#SQL) - 10)
select #SQL
--uncomment the line below when you satisfied the dynamic sql is written the way you want it.
--exec sp_executesql #SQL

How to get return value from EXEC sp_executesql

How do I get the return value from EXEC sp_executesql #OpenQry so I can check if the value exists in IF EXISTS?
DECLARE #TableName VARCHAR(25)
DECLARE #TD_QUERY NVARCHAR(MAX)
DECLARE CUR_QRY CURSOR FOR
SELECT TABLENAME FROM dbo.tbl_table
OPEN CUR_QRY
FETCH NEXT FROM CUR_QRY INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #OpenQry = 'SELECT * FROM OPENQUERY(linkedserver,''SELECT TABLENAME FROM DBC.TABLES WHERE TABLEKIND=''''T'''' AND DATABASENAME=''''dbname'''' AND TABLENAME=''''' + #TableName + ''''''')'
EXEC sp_executesql #OpenQry
IF EXISTS (SELECT #OpenQry)
AND EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableName)
FETCH NEXT FROM CUR_QRY INTO #TableName
END
CLOSE CUR_QRY
DEALLOCATE CUR_QRY
you just need put the variable that you want receive de return value before sp_executesql
try tis code and let me know if works
DECLARE #TableName VARCHAR(MAX)
DECLARE #TableNameToDrop VARCHAR(MAX) --NEW
DECLARE #TD_QUERY NVARCHAR(MAX)
DECLARE #OpenQry NVARCHAR(MAX) -- Dont forget to declare this variable
declare #sqldrop nvarchar(max)
DECLARE CUR_QRY CURSOR FOR
SELECT TABLENAME FROM dbo.tbl_table
OPEN CUR_QRY
FETCH NEXT FROM CUR_QRY INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #OpenQry = 'SELECT * FROM OPENQUERY(linkedserver,''SELECT TABLENAME FROM DBC.TABLES WHERE TABLEKIND=''''T'''' AND DATABASENAME=''''dbname'''' AND TABLENAME=''''' + #TableName + ''''''')'
EXEC TableNameToDrop = sp_executesql #OpenQry
/*
IF EXISTS (SELECT #OpenQry)
AND EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableName )
*/
--maybe you dont need check if the table name existis, jut try this
IF EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableNameToDrop )
BEGIN
set #sqldrop = 'drop table '+ #TableNameToDrop
EXEC sp_executesql #sqldrop
END
FETCH NEXT FROM CUR_QRY INTO #TableName
END
CLOSE CUR_QRY
DEALLOCATE CUR_QRY
Regards

Can I apply an update statement to multiple databases at the same time?

Say I want to run the following:
update users set age = 10
on databases:
db1, db2, db3
All on the same server, I want to loop through and perform the same action.
Currently I am doing this manually using management studio via the dropdown.
Hoping there is a better way.
You could probably do it with dynamic SQL. Something like so:
create table #dbs (db_name sysname not null)
insert into #dbs values ('db1'),('db2'),('db3')
declare curs cursor for
select db_name from #dbs
declare #db sysname, #sql nvarchar(max)
open curs
while(1=1)
begin
fetch next from curs into #db
if (##fetch_status <> 0)
break
set #sql = 'update ' + quotename(#db) + '.dbo.users set age = 10'
exec(#sql)
end
close curs
deallocate curs
drop table #dbs
Not sure about doing it 'dynamically', i.e. a FOR-EACH style loop on all the databases in a server, but this should work:
USE db1
update users set age = 10
GO
USE db2
update users set age = 10
GO
USE db3
update users set age = 10
Designate a server as a central management server and then add the other servers to the server group. Then you can run the update on all databases within the group. http://msdn.microsoft.com/en-us/library/bb934126.aspx
use [WWAUTHxxx__] -- a db containing active databases.
set nocount on
declare #Catalog as nvarchar(32)
declare #LibraryName as varchar(255)
declare #dbtable as varchar(50)
declare #retval as nvarchar(50)
declare #sSQL as nvarchar(max)
declare #parmdef as nvarchar(500)
declare #retvalout as nvarchar(50)
Declare Library_Cursor Cursor for
select top(1000) xCatalog, xLibraryName
from Active_DBs
order by xcatalog
Open Library_Cursor;
Fetch Next from Library_Cursor into #Catalog, #LibraryName
while ##Fetch_status = 0
begin
set #dbTable = #Catalog + '.dbo.las_circperiods'
set #ParmDef = N'#retvalOUT int OUTPUT';
set #sSQL = N'Select #retvalout = count(*) from ' + #dbtable
+ ' where xlastcircdate is null'
exec sp_executesql #ssql,#parmdef,#retvalout=#retval output
if #retval > 0 -- check/print Sql and then activate.
-- I like checking to see the potentially affected databases.
begin
print #Catalog + ',' + #LibraryName + ',' + #retval
set #ssql = N'update ' + #dbTable
+ ' set xlastcircdate = '''' '
+ ' where xlastcircdate is null'
-- print #ssql -- View what you might will do
exec sp_executesql #ssql -- Do it.
end
Fetch Next from Library_Cursor into #Catalog, #LibraryName
end;
close Library_cursor
Deallocate Library_cursor

Error in Declaration of cursor

set #SQL=N' select #minTableId = MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
Declare Cursor For
EXEC SP_EXECUTESQL #SQL
if i have declared all the variables in above query but Declaration of cursor in above
query shows ERROR.
What is Solution?
In order to execute a cursor over dynamic SQL you must put the output of your dynamic sql into a temporary table and then cursor over the temporary table like this:
DECLARE #TableName NVARCHAR(100)
DECLARE #SQL NVARCHAR(1000)
CREATE TABLE #TempTABLE(email NVARCHAR(200))
SET #TableName='Users'
SELECT #SQL='INSERT INTO #TempTable SELECT email FROM ' + #TableName
EXEC (#SQL)
DECLARE MyCursor CURSOR FOR SELECT * FROM #TempTable
OPEN MyCursor
DECLARE #Email NVARCHAR(200)
FETCH NEXT FROM MyCursor INTO #Email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Email = ' + #Email
FETCH NEXT FROM MyCursor INTO #Email
END
CLOSE MyCursor
DEALLOCATE MyCursor
DROP TABLE #TempTABLE
I dont think you need a cursor for this
try
DECLARE #AcDB VARCHAR(10),
#Sql NVARCHAR(MAX)
set #SQL=N' select MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
DECLARE #Temp TABLE(
MinID INT
)
INSERT INTO #Temp EXEC SP_EXECUTESQL #SQL
DECLARE #minTableId INT
SELECT TOP 1 #minTableId = MinID FROM #Temp
SELECT #minTableId
EDIT: Also here is the actual CURSOR documentation
DECLARE CURSOR (Transact-SQL)

Resources