2013-01-24 8 views
5

Ho un tipo di tabella definito dall'utente in un database in SQL Server (chiamiamolo questo DB1).Passare un tipo di tabella definito dall'utente tra il database di SQL Server

La definizione per il mio tipo è molto semplice e include solo 2 colonne. Lo script per creare il mio tipo è qui sotto:

CREATE TYPE [dbo].[CustomList] AS TABLE 
(
    [ID] [int] , 
    [Display] [NVARCHAR] (100) 
) 

Ho anche eseguire lo stesso script su un altro database, quindi il mio tipo è su 2 basi di dati (Chiamiamo il secondo database di DB2).

Ora chiamo una stored procedure in DB1 dalla mia app C# passando nel parametro per il mio tipo definito dall'utente CustomList.

La procedura in DB1 ora deve chiamare una procedura su DB2 passando in questo CustomList.

Quindi, la procedura in DB1 assomiglia a questo:

ALTER PROCEDURE [dbo].[selectData] 
    @psCustomList CustomList ReadOnly 
AS 
BEGIN 
    EXEC DB2.dbo.selectMoreData @psCustomList 
END 

e la procedura in DB2 è come questo (ho solo mostrare la lista dei parametri come questo è tutto ciò che è necessario):

ALTER PROCEDURE [dbo].[selectMoreData] 
    @psCustomList CustomList ReadOnly 
AS 
BEGIN 
...... 

Quando eseguo questo ricevo il seguente errore:

Operand type clash: CustomList is incompatible with CustomList

Qualcuno ha qualche idea su quello che sto facendo male?

sto usando 2008.

Grazie SQL Server in anticipo

+0

possibile duplicato del [Passando tabella dei parametri con valori di stored procedure attraverso diversi database] (http://stackoverflow.com/questions/9531769/passing-table-valued-parameter-to- stored-procedure-across-different-databases) –

+0

"così il mio tipo è su 2 database" - no, due dei tuoi database hanno tipi di tabella definiti con nomi e strutture identici. Non c'è il concetto che siano dello stesso tipo. E non puoi definire una variabile di un tipo di database diverso, quindi non puoi nemmeno creare una variabile del tipo giusto e copiare i dati attraverso. –

risposta

7

Questo è un duplicato di Can you create a CLR UDT to allow for a shared Table type across databases?

In sostanza, Tipi di tabella definiti dall'utente non possono essere condivisi tra i database. Gli UDT basati su CLR possono essere condivisi tra i database,, ma solo se sono state soddisfatte determinate condizioni, ad esempio lo stesso assembly viene caricato in entrambi i database e alcune altre cose (i dettagli sono nella domanda duplicata sopra riportata).

Per questa particolare situazione, 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 l'istruzione USE, ma ciò può essere fatto solo in SQL dinamico se è necessario farlo 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 

UPDATE

Utilizzando sp_executesql, sia nel tentativo di evitare la tabella temporanea locale semplicemente passando l'UDTT come TVP, o semplicemente come un mezzo per fare una query con parametri, non fa funziona davvero (anche se certamente 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; 

falliranno su "@ TableTypeDB2 ha un tipo di dati non valido", anche se è visualizzato correttamente che DB2 è il database "corrente".Ha qualcosa a che fare con il modo in cui sp_executesql determina i tipi di dati variabili poiché l'errore si riferisce a @TableTypeDB2 come "variabile # 2", anche se viene creato localmente e non come parametro di input.

Infatti, sp_executesql errore se viene dichiarata una singola variabile (tramite il parametro di input della lista parametri su sp_executesql), anche se non viene mai fatto riferimento, per non parlare di utilizzato. Cioè, il seguente codice verrà eseguito nello stesso errore di non riuscire a trovare la definizione per l'UDTT che accade 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 ricordare che sp_executesql non fa lavoro quando si passa in variabili)

TUTTAVIA, questo problema può essere risolto (beh, se non si sta tentando di passare in TVP per evitare la tabella temporanea - 2 query sopra) cambiando il database di esecuzione di sp_executesql in modo che il processo sia locale al DB in cui esiste l'altro TVP. Una cosa bella di sp_executesql è che, a differenza di EXEC, si tratta di una stored procedure e di una stored procedure di sistema, quindi può essere pienamente qualificato. Facendo uso di questo fatto, è possibile utilizzare sp_executesql, il che significa anche che non è necessaria l'istruzione USE [DB2]; all'interno di SQL dinamico. Il seguente codice 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 
+1

Si noti che se si utilizza 'sp_executesql', apparentemente non funzionerà se si tenta di utilizzare un blocco di parametri –

+0

@MarkSowul ho testato e vedere cosa intendi. Ho aggiornato per aggiungere queste informazioni. Grazie! –

+0

Non proprio - l'uso di * qualsiasi * parametro in 'sp_executesql' lo farà. Stavo ancora usando un tavolo temporaneo; Stavo semplicemente passando in qualcosa d'altro innocuo. Quindi riceverai strani errori * all'interno * dell'SQL dinamico, lamentando l'uso dei tipi di tabella (anche se hai cambiato db con 'USE') –