Toto je duplikát Můžete vytvořit CLR UDT, abyste umožnili sdílený typ tabulky napříč databázemi?
Uživatelsky definované typy tabulek v podstatě nelze sdílet mezi databázemi. UDT založené na CLR umí být sdíleny napříč databázemi, ale pouze pokud byly splněny určité podmínky, jako je načtení stejného shromáždění do obou databází a několik dalších věcí (podrobnosti jsou v duplicitní otázce uvedené výše).
Pro tuto konkrétní situaci existuje způsob, jak předat informace z DB1
do DB2
, i když to není elegantní řešení. Abyste mohli použít typ tabulky, váš aktuální databázový kontext musí být databází, ve které daný typ tabulky existuje. To se provádí pomocí USE
příkaz, ale to lze provést pouze v dynamickém SQL, pokud je to nutné provést v rámci uložené procedury.
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
AKTUALIZACE
Pomocí sp_executesql
, buď ve snaze vyhnout se místní dočasné tabulce pouhým předáním UDTT jako TVP, nebo jednoduše jako prostředek k provedení parametrizovaného dotazu, ve skutečnosti nefunguje (ačkoli to určitě vypadá, že by mělo). To znamená:
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;
selže na "@TableTypeDB2 má neplatný datový typ", i když správně zobrazuje, že DB2
je "aktuální" databáze. Má to něco společného s tím, jak sp_executesql
určuje proměnné datové typy, protože chyba odkazuje na @TableTypeDB2
jako "proměnná # 2", i když je vytvořena lokálně a ne jako vstupní parametr.
Ve skutečnosti sp_executesql
dojde k chybě, pokud je deklarována jedna proměnná (přes vstupní parametr seznamu parametrů do sp_executesql
), i když na něj není nikdy odkazováno, natož aby bylo použito. To znamená, že následující kód narazí na stejnou chybu, že nebude schopen najít definici pro UDTT, která se stane s dotazem bezprostředně výše:
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
(Děkuji @Marku Sowulovi za zmínku, že sp_executesql
nefunguje při předávání proměnných)
NICMÉNĚ tento problém lze obejít (dobře, pokud se nepokoušíte předat TVP, abyste se vyhnuli dočasné tabulce -- 2 dotazy výše) změnou spouštěcí databáze sp_executesql takže proces bude lokální pro DB, ve které existuje další TVP. Jedna pěkná věc na
sp_executesql
je to na rozdíl od EXEC
, je to uložená procedura a navíc systémová uložená procedura, takže ji lze plně kvalifikovat. Využití této skutečnosti umožňuje sp_executesql
fungovat, což také znamená, že není potřeba USE [DB2];
v rámci dynamického SQL. Následující kód funguje:
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