Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Przekazywanie typu tabeli zdefiniowanego przez użytkownika między bazą danych SQL Server

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wiele wartości kolumn w jednym wierszu

  2. Czy scalanie i scalanie są takie same w programie SQL Server?

  3. Znajdowanie zduplikowanych wierszy w SQL Server

  4. Rozwiązywanie problemów z przyznawaniem pamięci zmiennej w programie SQL Server

  5. Wykonanie funkcji COUNT SQL