sql >> Databáze >  >> RDS >> Sqlserver

Předávání uživatelem definovaného typu tabulky mezi databází SQL Server

Toto je duplikát Můžete vytvořit CLR UDT, abyste umožnili sdílený typ tabulky napříč databázemi?

Uživatelsky definované typy tabulek v podstatě nelze sdílet mezi databázemi. UDT založené na CLR umí být sdíleny napříč databázemi, ale pouze pokud byly splněny určité podmínky, jako je načtení stejného shromáždění do obou databází a několik dalších věcí (podrobnosti jsou v duplicitní otázce uvedené výše).

Pro tuto konkrétní situaci existuje způsob, jak předat informace z DB1 do DB2 , i když to není elegantní řešení. Abyste mohli použít typ tabulky, váš aktuální databázový kontext musí být databází, ve které daný typ tabulky existuje. To se provádí pomocí USE příkaz, ale to lze provést pouze v dynamickém SQL, pokud je to nutné provést v rámci uložené procedury.

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

AKTUALIZACE

Pomocí sp_executesql , buď ve snaze vyhnout se místní dočasné tabulce pouhým předáním UDTT jako TVP, nebo jednoduše jako prostředek k provedení parametrizovaného dotazu, ve skutečnosti nefunguje (ačkoli to určitě vypadá, že by mělo). To znamená:

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;

selže na "@TableTypeDB2 má neplatný datový typ", i když správně zobrazuje, že DB2 je "aktuální" databáze. Má to něco společného s tím, jak sp_executesql určuje proměnné datové typy, protože chyba odkazuje na @TableTypeDB2 jako "proměnná # 2", i když je vytvořena lokálně a ne jako vstupní parametr.

Ve skutečnosti sp_executesql dojde k chybě, pokud je deklarována jedna proměnná (přes vstupní parametr seznamu parametrů do sp_executesql ), i když na něj není nikdy odkazováno, natož aby bylo použito. To znamená, že následující kód narazí na stejnou chybu, že nebude schopen najít definici pro UDTT, která se stane s dotazem bezprostředně výše:

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

(Děkuji @Marku Sowulovi za zmínku, že sp_executesql nefunguje při předávání proměnných)

NICMÉNĚ tento problém lze obejít (dobře, pokud se nepokoušíte předat TVP, abyste se vyhnuli dočasné tabulce -- 2 dotazy výše) změnou spouštěcí databáze sp_executesql takže proces bude lokální pro DB, ve které existuje další TVP. Jedna pěkná věc na sp_executesql je to na rozdíl od EXEC , je to uložená procedura a navíc systémová uložená procedura, takže ji lze plně kvalifikovat. Využití této skutečnosti umožňuje sp_executesql fungovat, což také znamená, že není potřeba USE [DB2]; v rámci dynamického SQL. Následující kód funguje:

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. MySQL SELECT AS kombinuje dva sloupce do jednoho

  2. Neplatný měsíc při provádění procedury parametru IN s hodnotou data

  3. jak vrátit dynamickou sadu výsledků ve funkci Oracle

  4. Jak CONVERT() funguje v MariaDB