sql >> Database >  >> RDS >> Sqlserver

Een door de gebruiker gedefinieerd tabeltype doorgeven tussen SQL Server-database

Dit is een duplicaat van Kun je een CLR UDT maken om een ​​gedeeld tabeltype tussen databases mogelijk te maken?

In wezen kunnen door de gebruiker gedefinieerde tabeltypen niet worden gedeeld tussen databases. CLR-gebaseerde UDT's kunnen worden gedeeld door databases, maar alleen als aan bepaalde voorwaarden is voldaan, zoals het laden van dezelfde Assembly in beide databases, en een paar andere dingen (details staan ​​in de dubbele vraag hierboven vermeld).

Voor deze specifieke situatie is er een manier om de informatie van DB1 door te geven naar DB2 , hoewel het geen elegante oplossing is. Om een ​​tabeltype te gebruiken, moet uw huidige databasecontext de database zijn waarin het tabeltype bestaat. Dit gebeurt via de USE statement, maar dat kan alleen in dynamische SQL als het binnen een Opgeslagen Procedure moet worden gedaan.

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

UPDATE

sp_executesql gebruiken , hetzij in een poging om de lokale tijdelijke tabel te vermijden door simpelweg de UDTT door te geven als een TVP, of gewoon als een manier om een ​​geparametriseerde query uit te voeren, werkt niet echt (hoewel het er zeker uitziet zoals het zou moeten). Betekenis, het volgende:

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;

zal mislukken op "@TableTypeDB2 has an invalid datatype", ook al geeft het correct weer dat DB2 is de "huidige" database. Het heeft iets te maken met hoe sp_executesql bepaalt variabele datatypes sinds de fout verwijst naar @TableTypeDB2 als "variabele # 2", ook al is het lokaal gemaakt en niet als invoerparameter.

In feite, sp_executesql zal een foutmelding geven als een enkele variabele wordt gedeclareerd (via de parameterlijst invoerparameter naar sp_executesql ), zelfs als er nooit naar wordt verwezen, laat staan ​​dat het wordt gebruikt. Dit betekent dat de volgende code dezelfde fout zal tegenkomen als het niet kunnen vinden van de definitie voor de UDTT die optreedt met de query direct hierboven:

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

(Met dank aan @Mark Sowul voor het vermelden dat sp_executesql werkt niet bij het doorgeven van variabelen)

ECHTER, dit probleem kan worden omzeild (nou ja, zolang je niet probeert de TVP door te geven om de tijdelijke tabel te vermijden -- 2 query's hierboven) door de uitvoeringsdatabase van sp_executesql zodat het proces lokaal is voor de DB waarin de andere TVP bestaat. Een leuk ding over sp_executesql is dat, in tegenstelling tot EXEC , het is een Opgeslagen Procedure, en een systeem opgeslagen procedure, dus het kan volledig worden gekwalificeerd. Door gebruik te maken van dit feit kan sp_executesql om te werken, wat ook betekent dat het USE [DB2]; . niet nodig is statement binnen de dynamische SQL. De volgende code werkt wel:

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. Rijwaarden samenvoegen T-SQL

  2. Hoe MATCH AGAINST werkt in MariaDB

  3. Hoe SYS_REFCURSOR afdrukken met het PLSQLDeveloper-venster?

  4. MySQL-juweeltje op OSX 10.7 Lion