To jest duplikat Czy możesz utworzyć CLR UDT, aby umożliwić współdzielenie typu tabeli w bazach danych?
Zasadniczo typy tabel zdefiniowane przez użytkownika nie mogą być współużytkowane przez bazy danych. UDT oparte na CLR mogą być współużytkowane przez bazy danych, ale tylko wtedy, gdy spełnione zostały pewne warunki, takie jak ten sam zestaw ładowany do obu baz danych i kilka innych rzeczy (szczegóły znajdują się w pytaniu zduplikowanym wymienionym powyżej).
W tej konkretnej sytuacji istnieje sposób na przekazanie informacji z DB1
do DB2
, choć nie jest to eleganckie rozwiązanie. Aby użyć typu tabeli, bieżący kontekst bazy danych musi być bazą danych, w której istnieje typ tabeli. Odbywa się to za pomocą USE
instrukcji, ale można to zrobić tylko w dynamicznym SQL, jeśli trzeba to zrobić w ramach procedury składowanej.
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
AKTUALIZUJ
Używanie sp_executesql
, czy to w celu uniknięcia lokalnej tabeli tymczasowej przez zwykłe przekazanie UDTT jako TVP, czy po prostu jako sposób wykonania sparametryzowanego zapytania, w rzeczywistości nie działa (choć z pewnością wygląda tak, jak powinien). Znaczenie:
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;
nie powiedzie się, gdy „@TableTypeDB2 ma nieprawidłowy typ danych”, mimo że poprawnie wyświetla ten DB2
to „bieżąca” baza danych. Ma to coś wspólnego z tym, jak sp_executesql
określa zmienne typy danych, ponieważ błąd odnosi się do @TableTypeDB2
jako "zmienna nr 2", mimo że jest tworzona lokalnie, a nie jako parametr wejściowy.
W rzeczywistości sp_executesql
spowoduje błąd, jeśli zostanie zadeklarowana pojedyncza zmienna (poprzez parametr wejściowy listy parametrów do sp_executesql
), nawet jeśli nigdy się nie odwołuje, nie mówiąc już o używaniu. Oznacza to, że poniższy kod napotka ten sam błąd polegający na niemożności znalezienia definicji UDTT, co ma miejsce w przypadku zapytania znajdującego się bezpośrednio powyżej:
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
(Podziękowania dla @Mark Sowul za wspomnienie, że sp_executesql
nie działa podczas przekazywania zmiennych)
JEDNAK ten problem można obejść (dobrze, o ile nie próbujesz przekazać w TVP w celu uniknięcia tabeli tymczasowej -- 2 zapytania powyżej) poprzez zmianę bazy danych wykonania sp_executesql tak, aby proces był lokalny dla DB, w którym istnieje inny TVP. Jedna fajna rzecz o
sp_executesql
jest to, w przeciwieństwie do EXEC
, jest to procedura składowana, a do tego systemowa procedura składowana, więc może być w pełni kwalifikowana. Wykorzystanie tego faktu pozwala sp_executesql
do pracy, co oznacza również, że nie ma potrzeby używania USE [DB2];
instrukcja w ramach Dynamic SQL. Poniższy kod działa:
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