sqlsql-serverlinked-serverexecute

Iterate through list of linkservers while trying to execute SQL query as EXECUTE (@SQL) at [LinkServer]


I am trying to execute the below -

DECLARE @SQL nvarchar(max), @LinkServer nvarchar(200), @min INT=1, @max INT

drop table if exists #clients;
create table #clients (Id int identity(1,1), ClientName varchar(200), LinkServer varchar(200));

insert into #clients (ClientName, LinkServer) values ('abc', 'LinkServer1');
insert into #clients (ClientName, LinkServer) values ('xyz', 'LinkServer2');

set @max = (SELECT MAX(Id) FROM #clients)

WHILE(@min<=@max)
BEGIN
@SQL = 'select * from Table;'

SELECT @LinkServer=LinkServer FROM #clients WHERE ID=@min
EXECUTE (@SQL) AT [@LinkServer]

SET @min=@min+1
END

But it reads @LinkServer as a character. I want to run this query on multiple server, but unable to do so with the current syntax. I need to use EXECUTE(@SQL) AT [LinkServer] only.


Solution

  • You can't parameterize the AT linked_server_name like that, it has to be a literal.

    You can do this more easily using a parameterized EXEC. Not many people know that the procedure name itself can be parameterized. So you just need to build a string Server..sys.sp_executesql

    Note that the server name needs to be in a sysname or nvarchar(128), and you need to quote it using QUOTENAME. Also, don't use WHILE just to avoid a cursor, it's silly and even more inefficient than a cursor.

    DECLARE @proc nvarchar(1000);
    
    DECLARE @crsr CURSOR;
    SET @crsr = CURSOR FAST_FORWARD FOR
        SELECT procName = QUOTENAME(LinkServer) + '..sys.sp_executesql'
        FROM #clients;
    
    OPEN @crsr;
    WHILE(1=1)
    BEGIN
        FETCH NEXT @crsr INTO @proc;
        IF @@FETCH_STATUS <> 0
            BREAK;
    
        EXEC @proc N'select * from Table;'
    END;
    

    db<>fiddle