Eu tenho um banco de dados principal ( MAIN
) e vários bancos de dados clientes ( CLIENTDB
). Eu preciso iniciar a consulta MAIN
e obter dados de CLIENTDB
. O banco de dados do cliente também pode ser encontrado no servidor vinculado. Meu problema é que preciso fazer isso em uma função com EXEC
, para poder pegar SELECT * FROM SRV.CLIENTDB
, mas isso não é permitido pelo SQL Server. Existe outra maneira de fazer isso?
Eu preciso ter um código como dentro da função:
DECLARE @sSRV AS VARCHAR(128) = 'SRV';
DECLARE @sDB AS VARCHAR(128) = 'CLIENTDB1';
EXEC('SELECT * FROM ' + sSRV + '.' + @sDB + '.MyTable');
Preciso de uma função, pois preciso fazer joins com ela dentro do meu motor.
Não é possível fazer isso dinamicamente via
EXEC
, pois IIRC você não pode fazer isso em uma função. Você não pode usar uma consulta distribuída ad-hoc viaOPENROWSET
( https://msdn.microsoft.com/en-us/library/ms187569.aspx ), poisOPENROWSET
exige explicitamente que suas entradas, incluindo o texto da consulta, sejam literais de string não mais gerais valores de string (portanto, você não pode usar uma expressão ou variável).As únicas maneiras que consigo pensar envolvem ter uma função por banco de dados remoto ou a
CASE/IF
sugestão do comentário de Marian. Você poderia criar essa função de forma pragmática, passando por uma exibição baseada emsys.servers
e outros DMVs para reunir a função por meio da concatenação de strings e, em seguida, substituir a versão antiga pela nova usando uma chamada paraEXEC
. Você ainda precisa executar o procedimento de geração toda vez que a lista de servidores vinculados for alterada, mas pelo menos seria apenas uma chamada de procedimento (ou execução de script/instrução) e não uma alteração de código mais manual.Isso tudo soa muito hacky embora. Esse "cheiro de código" sugeriria que um redesenho está em ordem se você tiver esse poder!
Acho que este post responde bem à pergunta. Usando
https://www.mssqltips.com/sqlservertip/1757/dynamic-sql-execution-on-remote-sql-server-using-exec-at/
Aqui está uma parte do post:
Obrigado,
Dado este requisito:
Eu diria que o próprio requisito (de que precisa ser uma função) é equivocado devido ao objetivo declarado (de que os dados precisam ser unidos a tabelas e/ou exibições locais). A questão é que os TVFs (sejam eles T-SQL ou SQLCLR) se comportam de maneira a não serem propícios à junção. Elas:
Por esses dois motivos, geralmente é melhor despejar os resultados em uma tabela temporária local e, em seguida, JOIN nela. Portanto, você pode fazer isso com bastante facilidade fazendo o seguinte:
'SELECT * FROM ' + sSRV + '.' + @sDB + '.MyTable'
(é claro, incluindo um nome de esquema antes do nome da tabela ;-)INSERT INTO #LocalTempTable (column_list) EXEC dbo.NewStoredProcedure;
Isso fornecerá o que você deseja de uma maneira que terá um desempenho melhor do que a solicitação inicial, além de permitir que a lista de servidores e bancos de dados flutue com o tempo.