Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Übergeben eines benutzerdefinierten Tabellentyps zwischen SQL Server-Datenbanken

Dies ist ein Duplikat von Können Sie einen CLR-UDT erstellen, um einen gemeinsam genutzten Tabellentyp über Datenbanken hinweg zu ermöglichen?

Grundsätzlich können benutzerdefinierte Tabellentypen nicht von Datenbanken gemeinsam genutzt werden. CLR-basierte UDTs können von Datenbanken gemeinsam genutzt werden, aber nur, wenn bestimmte Bedingungen erfüllt sind, wie z. B. dass dieselbe Assembly in beide Datenbanken geladen wird, und einige andere Dinge (Details finden Sie in der oben erwähnten doppelten Frage).

Für diese spezielle Situation gibt es eine Möglichkeit, die Informationen von DB1 zu übergeben zu DB2 , obwohl es keine elegante Lösung ist. Um einen Tabellentyp verwenden zu können, muss Ihr aktueller Datenbankkontext die Datenbank sein, in der der Tabellentyp vorhanden ist. Dies geschieht über den USE -Anweisung, aber das kann nur in dynamischem SQL erfolgen, wenn dies innerhalb einer gespeicherten Prozedur erfolgen muss.

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

AKTUALISIEREN

Verwenden von sp_executesql , entweder in einem Versuch, die lokale temporäre Tabelle zu umgehen, indem einfach die UDTT als TVP übergeben wird, oder einfach als Mittel, um eine parametrisierte Abfrage durchzuführen, funktioniert nicht wirklich (obwohl es sicherlich so aussieht, als ob es sollte). Das bedeutet Folgendes:

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;

schlägt fehl, wenn „@TableTypeDB2 einen ungültigen Datentyp hat“, obwohl es diesen DB2 korrekt anzeigt ist die "aktuelle" Datenbank. Es hat etwas damit zu tun, wie sp_executesql bestimmt variable Datentypen seit dem Fehler, der sich auf @TableTypeDB2 bezieht als "Variable # 2", obwohl sie lokal und nicht als Eingabeparameter erstellt wird.

Tatsächlich sp_executesql wird einen Fehler verursachen, wenn eine einzelne Variable deklariert wird (über den Eingabeparameter der Parameterliste für sp_executesql ), auch wenn es nie referenziert, geschweige denn verwendet wird. Das heißt, der folgende Code wird auf den gleichen Fehler stoßen, dass er die Definition für die UDTT nicht finden kann, der bei der unmittelbar darüber liegenden Abfrage auftritt:

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

(Dank an @Mark Sowul für die Erwähnung, dass sp_executesql funktioniert nicht beim Übergeben von Variablen)

JEDOCH kann dieses Problem umgangen werden (solange Sie nicht versuchen, das TVP zu übergeben, um die temporäre Tabelle zu vermeiden – 2 Abfragen oben), indem Sie die Ausführungsdatenbank von sp_executesql so dass der Prozess lokal für die DB ist, in der das andere TVP existiert. Eine nette Sache über sp_executesql ist das im Gegensatz zu EXEC , es ist eine gespeicherte Prozedur und noch dazu eine gespeicherte Systemprozedur, sodass sie vollständig qualifiziert werden kann. Die Ausnutzung dieser Tatsache ermöglicht sp_executesql funktionieren, was auch bedeutet, dass USE [DB2]; nicht benötigt wird -Anweisung innerhalb des dynamischen SQL. Der folgende Code funktioniert:

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