Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Passando um tipo de tabela definido pelo usuário entre o banco de dados SQL Server


Esta é uma duplicata de Você pode criar um CLR UDT para permitir um tipo de tabela compartilhado entre bancos de dados?

Essencialmente, os tipos de tabela definidos pelo usuário não podem ser compartilhados entre bancos de dados. UDTs baseados em CLR podem ser compartilhado entre bancos de dados, mas somente se determinadas condições forem atendidas, como o mesmo Assembly sendo carregado em ambos os bancos de dados e algumas outras coisas (os detalhes estão na pergunta duplicada mencionada acima).

Para esta situação específica, existe uma maneira de passar as informações do DB1 para DB2 , embora não seja uma solução elegante. Para usar um tipo de tabela, seu contexto de banco de dados atual precisa ser o banco de dados no qual o tipo de tabela existe. Isso é feito através do USE instrução, mas isso só pode ser feito em SQL dinâmico se precisar ser feito dentro de um procedimento armazenado.
USE [DB1];
GO

CREATE PROCEDURE [dbo].[selectData]
    @psCustomList CustomList READONLY
AS
BEGIN
    -- create a temp table as it can be referenced in dynamic SQL
    CREATE TABLE #TempCustomList
    (
        [ID] [INT],
        [Display] [NVARCHAR] (100)
    );

    INSERT INTO #TempCustomList (ID, Display)
        SELECT ID, Display FROM @psCustomList;

    EXEC('
        USE [DB2];

        DECLARE @VarCustomList CustomList;

        INSERT INTO @VarCustomList (ID, Display)
            SELECT ID, Display FROM #TempCustomList;

        EXEC dbo.selectMoreData @VarCustomList;
     ');
END

ATUALIZAÇÃO

Usando sp_executesql , seja na tentativa de evitar a tabela temporária local simplesmente passando o UDTT como um TVP, ou simplesmente como um meio de fazer uma consulta parametrizada, na verdade não funciona (embora certamente pareça que deveria). Ou seja, o seguinte:
USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeA
(
    @TheUDTT dbo.TestTable1 READONLY
)
AS
SET NOCOUNT ON;

EXEC sp_executesql N'
  USE [DB2];
  SELECT DB_NAME() AS [CurrentDB];

  DECLARE @TableTypeDB2 dbo.TestTable2;
  INSERT INTO @TableTypeDB2 ([Col1])
    SELECT tmp.[Col1]
    FROM   @TableTypeDB1 tmp;

  --EXEC dbo.CrossDatabaseTableTypeB @TableTypeDB2;
  ',
  N'@TableTypeDB1 dbo.TestTable1 READONLY',
  @TableTypeDB1 = @TheUDTT;
GO


DECLARE @tmp dbo.TestTable1;
INSERT INTO @tmp ([Col1]) VALUES (1), (3);
SELECT * FROM @tmp;

EXEC dbo.CrossDatabaseTableTypeA @TheUDTT = @tmp;

falhará em "@TableTypeDB2 tem um tipo de dados inválido", mesmo que exiba corretamente que DB2 é o banco de dados "atual". Tem algo a ver com como sp_executesql determina tipos de dados de variáveis ​​desde que o erro se refere a @TableTypeDB2 como "variável # 2", mesmo sendo criada localmente e não como parâmetro de entrada.

Na verdade, sp_executesql apresentará um erro se uma única variável for declarada (através do parâmetro de entrada da lista de parâmetros para sp_executesql ), mesmo que nunca seja referenciado, muito menos usado. Ou seja, o código a seguir irá se deparar com o mesmo erro de não conseguir encontrar a definição para o UDTT que acontece com a consulta imediatamente acima:
USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeC
AS
SET NOCOUNT ON;

EXEC sp_executesql N'
  USE [DB2];
  SELECT DB_NAME() AS [CurrentDB];

  DECLARE @TableTypeDB2 dbo.TestTable2;
  ',
  N'@SomeVar INT',
  @SomeVar = 1;
GO

(Obrigado a @Mark Sowul por mencionar que sp_executesql não funciona ao passar variáveis)

NO ENTANTO, esse problema pode ser contornado (bem, desde que você não esteja tentando passar o TVP para evitar a tabela temporária -- 2 consultas acima) alterando o banco de dados de execução de sp_executesql para que o processo seja local para o banco de dados no qual o outro TVP existe. Uma coisa legal sobre sp_executesql é que, ao contrário de EXEC , é um procedimento armazenado e um procedimento armazenado do sistema, portanto, pode ser totalmente qualificado. Fazer uso deste fato permite sp_executesql funcionar, o que também significa que não há necessidade do USE [DB2]; instrução dentro do SQL dinâmico. O código a seguir funciona:
USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeD
(
    @TheUDTT dbo.TestTable1 READONLY
)
AS
SET NOCOUNT ON;

-- create a temp table as it can be referenced in dynamic SQL
CREATE TABLE #TempList
(
    [ID] [INT]
);

INSERT INTO #TempList ([ID])
   SELECT [Col1] FROM @TheUDTT;

EXEC [DB2].[dbo].sp_executesql N'
  SELECT DB_NAME() AS [CurrentDB];

  DECLARE @TableTypeDB2 dbo.TestTable2;
  INSERT INTO @TableTypeDB2 ([Col1])
    SELECT tmp.[ID]
    FROM   #TempList tmp;

  EXEC dbo.CrossDatabaseTableTypeB @TableTypeDB2;
  ',
  N'@SomeVariable INT',
  @SomeVariable = 1111;
GO