Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Passaggio di un tipo di tabella definito dall'utente tra il database di SQL Server

Questo è un duplicato di Puoi creare un UDT CLR per consentire un tipo di tabella condiviso tra i database?

In sostanza, i tipi di tabella definiti dall'utente non possono essere condivisi tra database. Gli UDT basati su CLR possono essere condivisi tra database, ma solo se sono state soddisfatte determinate condizioni, come il caricamento dello stesso assembly in entrambi i database e poche altre cose (i dettagli sono nella domanda duplicata sopra riportata).

Per questa situazione particolare, c'è un modo per passare le informazioni da DB1 a DB2 , anche se non è una soluzione elegante. Per utilizzare un tipo di tabella, il contesto del database corrente deve essere il database in cui esiste il tipo di tabella. Questo viene fatto tramite il USE istruzione, ma ciò può essere eseguito solo in SQL dinamico se necessario all'interno di una stored procedure.

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

AGGIORNAMENTO

Usando sp_executesql , nel tentativo di evitare la tabella temporanea locale semplicemente passando l'UDTT come TVP, o semplicemente come mezzo per eseguire una query parametrizzata, in realtà non funziona (anche se sembra che dovrebbe). Significato, il seguente:

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;

fallirà su "@TableTypeDB2 ha un tipo di dati non valido", anche se mostra correttamente quel DB2 è il Database "attuale". Ha qualcosa a che fare con il modo in cui sp_executesql determina i tipi di dati variabili dall'errore riferito a @TableTypeDB2 come "variabile # 2", anche se è creata localmente e non come parametro di input.

Infatti, sp_executesql si verificherà un errore se viene dichiarata una singola variabile (tramite l'elenco dei parametri parametro di input in sp_executesql ), anche se non è mai referenziato, tanto meno utilizzato. Ciò significa che il codice seguente incorrerà nello stesso errore di non essere in grado di trovare la definizione per l'UDTT che si verifica con la query immediatamente sopra:

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

(Grazie a @Mark Sowul per aver menzionato che sp_executesql non funziona quando si passano variabili)

TUTTAVIA, questo problema può essere aggirato (beh, a patto che tu non stia provando a passare il TVP per evitare la tabella temporanea -- 2 query sopra) modificando il database di esecuzione di sp_executesql in modo che il processo sia locale al DB in cui esiste l'altro TVP. Una cosa carina su sp_executesql è quello, a differenza di EXEC , è una stored procedure e una stored procedure di sistema, quindi può essere pienamente qualificata. L'utilizzo di questo fatto consente sp_executesql per funzionare, il che significa anche che non è necessario il USE [DB2]; istruzione all'interno dell'SQL dinamico. Il codice seguente funziona:

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