Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Passer un type de table défini par l'utilisateur entre la base de données SQL Server

Ceci est un doublon de Pouvez-vous créer un UDT CLR pour autoriser un type de table partagé entre les bases de données ?

Essentiellement, les types de table définis par l'utilisateur ne peuvent pas être partagés entre les bases de données. Les UDT basés sur CLR peuvent être partagé entre les bases de données, mais seulement si certaines conditions sont remplies, telles que le même assemblage chargé dans les deux bases de données, et quelques autres choses (les détails se trouvent dans la question en double indiquée ci-dessus).

Pour cette situation particulière, il existe un moyen de transmettre les informations de DB1 vers DB2 , même si ce n'est pas une solution élégante. Pour utiliser un type de table, votre contexte de base de données actuel doit être la base de données dans laquelle le type de table existe. Cela se fait via le USE , mais cela ne peut être fait qu'en SQL dynamique si cela doit être fait dans une procédure stockée.

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

MISE À JOUR

Utilisation de sp_executesql , soit dans une tentative d'éviter la table temporaire locale en passant simplement l'UDTT en tant que TVP, soit simplement comme moyen de faire une requête paramétrée, ne fonctionne pas réellement (bien que cela semble certainement le cas). Signifiant, ce qui suit :

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;

échouera sur "@TableTypeDB2 a un type de données invalide", même s'il affiche correctement ce DB2 est la base de données "actuelle". Cela a quelque chose à voir avec la façon dont sp_executesql détermine les types de données variables depuis l'erreur référée à @TableTypeDB2 comme "variable # 2", même si elle est créée localement et non comme paramètre d'entrée.

En fait, sp_executesql générera une erreur si une seule variable est déclarée (via le paramètre d'entrée de la liste de paramètres à sp_executesql ), même s'il n'est jamais référencé, encore moins utilisé. Cela signifie que le code suivant rencontrera la même erreur de ne pas pouvoir trouver la définition de l'UDTT qui se produit avec la requête immédiatement ci-dessus :

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

(Merci à @Mark Sowul d'avoir mentionné que sp_executesql ne fonctionne pas lors du passage de variables)

CEPENDANT, ce problème peut être contourné (enfin, tant que vous n'essayez pas de transmettre le TVP afin d'éviter la table temporaire - 2 requêtes ci-dessus) en modifiant la base de données d'exécution de sp_executesql afin que le processus soit local à la base de données dans laquelle l'autre TVP existe. Une bonne chose à propos de sp_executesql est que, contrairement à EXEC , il s'agit d'une procédure stockée et d'une procédure stockée système, de sorte qu'elle peut être entièrement qualifiée. L'utilisation de ce fait permet à sp_executesql pour fonctionner, ce qui signifie également qu'il n'y a pas besoin de USE [DB2]; instruction dans le Dynamic SQL. Le code suivant fonctionne :

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