Detta är en dubblett av Kan du skapa en CLR UDT för att möjliggöra en delad tabelltyp över databaser?
Användardefinierade tabelltyper kan i huvudsak inte delas mellan databaser. CLR-baserade UDT:er kan delas mellan databaser, men bara om vissa villkor har uppfyllts, till exempel att samma sammansättning laddas in i båda databaserna och några andra saker (detaljerna finns i dubblettfrågan ovan).
För den här speciella situationen finns det ett sätt att skicka informationen från DB1
till DB2
, även om det inte är en elegant lösning. För att kunna använda en tabelltyp måste din nuvarande databaskontext vara den databas i vilken tabelltypen finns. Detta görs via USE
sats, men det kan bara göras i dynamisk SQL om det behöver göras inom en lagrad procedur.
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
UPPDATERA
Använder sp_executesql
, antingen i ett försök att undvika den lokala temporära tabellen genom att helt enkelt skicka in UDTT som en TVP, eller helt enkelt som ett sätt att göra en parametriserad fråga, fungerar faktiskt inte (även om det verkligen ser ut som det borde). Det betyder följande:
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;
kommer att misslyckas på "@TableTypeDB2 har en ogiltig datatyp", även om den visar korrekt DB2
är den "nuvarande" databasen. Det har något att göra med hur sp_executesql
bestämmer variabeldatatyper sedan felet hänvisade till @TableTypeDB2
som "variabel # 2", även om den skapas lokalt och inte som en indataparameter.
Faktum är att sp_executesql
kommer att fel om en enskild variabel deklareras (via parameterlistans indataparameter till sp_executesql
), även om det aldrig hänvisas till, än mindre används. Det betyder att följande kod kommer att stöta på samma fel att inte kunna hitta definitionen för UDTT som sker med frågan omedelbart ovan:
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
(Tack till @Mark Sowul för att du nämnde att sp_executesql
fungerar inte när du skickar in variabler)
Detta problem kan dock lösas (nåja, så länge du inte försöker skicka in TVP för att undvika temptabellen -- 2 frågor ovan) genom att ändra exekveringsdatabasen för sp_executesql så att processen kommer att vara lokal för den DB där den andra TVP finns. En trevlig sak med
sp_executesql
är det, till skillnad från EXEC
, det är en lagrad procedur, och en systemlagrad procedur, så att den kan vara helt kvalificerad. Att använda detta faktum tillåter sp_executesql
att fungera, vilket också innebär att det inte finns något behov av USE [DB2];
uttalande i Dynamic SQL. Följande kod fungerar:
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