Đây là bản sao của Bạn có thể tạo CLR UDT để cho phép loại Bảng được chia sẻ trên các cơ sở dữ liệu không?
Về cơ bản, các loại bảng do người dùng xác định không thể được chia sẻ trên các cơ sở dữ liệu. UDT dựa trên CLR có thể được chia sẻ trên các cơ sở dữ liệu, nhưng chỉ khi một số điều kiện nhất định đã được đáp ứng, chẳng hạn như cùng một Assembly được tải vào cả hai cơ sở dữ liệu và một số thứ khác (chi tiết có trong câu hỏi trùng lặp đã nêu ở trên).
Đối với tình huống cụ thể này, có một cách để chuyển thông tin từ DB1
tới DB2
, mặc dù nó không phải là một giải pháp thanh lịch. Để sử dụng Loại Bảng, bối cảnh cơ sở dữ liệu hiện tại của bạn cần phải là cơ sở dữ liệu mà Loại Bảng tồn tại. Điều này được thực hiện thông qua SỬ DỤNG
nhưng điều đó chỉ có thể được thực hiện trong SQL động nếu cần được thực hiện trong một Thủ tục được lưu trữ.
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
CẬP NHẬT
Sử dụng sp_executesql
, trong một nỗ lực để tránh bảng tạm thời cục bộ bằng cách chỉ cần chuyển vào UDTT dưới dạng TVP, hoặc đơn giản là một phương tiện để thực hiện một truy vấn tham số, không thực sự hoạt động (mặc dù chắc chắn có vẻ như vậy). Ý nghĩa như sau:
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;
sẽ không thành công trên "@ TableTypeDB2 có kiểu dữ liệu không hợp lệ", mặc dù nó hiển thị chính xác rằng DB2
là Cơ sở dữ liệu "hiện tại". Nó liên quan đến cách sp_executesql
xác định các kiểu dữ liệu thay đổi vì lỗi được tham chiếu đến @ TableTypeDB2
là "biến số 2", mặc dù nó được tạo cục bộ và không phải là tham số đầu vào.
Trên thực tế, sp_executesql
sẽ lỗi nếu một biến được khai báo (thông qua tham số đầu vào danh sách tham số cho sp_executesql
), ngay cả khi nó không bao giờ được tham chiếu, hãy để một mình được sử dụng. Có nghĩa là, đoạn mã sau sẽ gặp phải lỗi tương tự là không thể tìm thấy định nghĩa cho UDTT xảy ra với truy vấn ngay bên trên:
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
(Cảm ơn @Mark Sowul đã đề cập đến sp_executesql
không hoạt động khi chuyển vào các biến)
TUY NHIÊN, vấn đề này có thể được giải quyết (miễn là bạn không cố gắng vượt qua TVP để tránh bảng tạm thời - 2 truy vấn ở trên) bằng cách thay đổi cơ sở dữ liệu thực thi của sp_executesql để quá trình sẽ là cục bộ đối với DB mà TVP khác tồn tại. Một điều hay về
sp_executesql
đó là, không giống như EXEC
, nó là một Thủ tục được lưu trữ và một thủ tục được lưu trữ trong hệ thống tại đó, vì vậy nó có thể hoàn toàn đủ điều kiện. Tận dụng thực tế này cho phép sp_executesql
để hoạt động, điều đó cũng có nghĩa là không cần USE [DB2];
trong SQL động. Đoạn mã sau hoạt động:
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