2013-01-24 42 views
5

Tôi có kiểu bảng do người dùng định nghĩa trong một cơ sở dữ liệu trong SQL Server (hãy gọi đây là DB1).Truyền một kiểu bảng do người dùng định nghĩa giữa cơ sở dữ liệu SQL Server

Định nghĩa cho loại của tôi rất đơn giản và chỉ bao gồm 2 cột. Các kịch bản để tạo ra loại của tôi là dưới đây:

CREATE TYPE [dbo].[CustomList] AS TABLE 
(
    [ID] [int] , 
    [Display] [NVARCHAR] (100) 
) 

Tôi cũng đã chạy cùng một kịch bản trên cơ sở dữ liệu khác, do đó loại của tôi là trên 2 cơ sở dữ liệu (Hãy gọi cơ sở dữ liệu 2 DB2).

Tôi hiện đang gọi thủ tục được lưu trữ trong DB1 từ ứng dụng C# của tôi chuyển tham số cho loại CustomList do người dùng xác định của tôi.

Quy trình trong DB1 hiện cần gọi thủ tục DB2 chuyển qua số CustomList này.

Vì vậy, các thủ tục trong DB1 trông như thế này:

ALTER PROCEDURE [dbo].[selectData] 
    @psCustomList CustomList ReadOnly 
AS 
BEGIN 
    EXEC DB2.dbo.selectMoreData @psCustomList 
END 

Và các thủ tục trong DB2 là như thế này (tôi đã chỉ hiển thị danh sách tham số như đó là tất cả những gì cần thiết):

ALTER PROCEDURE [dbo].[selectMoreData] 
    @psCustomList CustomList ReadOnly 
AS 
BEGIN 
...... 

Khi tôi chạy này, tôi nhận được lỗi sau:

Operand type clash: CustomList is incompatible with CustomList

Bất cứ ai có bất kỳ ý tưởng những gì tôi đang làm sai?

Tôi đang sử dụng SQL Server 2008.

Cảm ơn trước

+0

có thể trùng lặp của [Bảng thông số Tham số có giá trị để lưu trữ trên các cơ sở dữ liệu khác nhau] (http://stackoverflow.com/questions/9531769/passing-table-valued-parameter-to- –

+0

"vì vậy loại của tôi là trên 2 cơ sở dữ liệu" - không, hai cơ sở dữ liệu của bạn xảy ra để có các loại bảng được xác định với tên và cấu trúc giống nhau. Không có khái niệm nào về chúng cùng loại. Và bạn không thể xác định một biến của một kiểu cơ sở dữ liệu khác nhau, vì vậy bạn thậm chí không thể tạo một biến kiểu đúng và sao chép dữ liệu trên. –

Trả lời

7

Đây là một bản sao của Can you create a CLR UDT to allow for a shared Table type across databases?

Về cơ bản, người dùng định nghĩa các loại Bảng không thể chia sẻ trên cơ sở dữ liệu. UDTs dựa trên CLR có thể được chia sẻ trên cơ sở dữ liệu, nhưng chỉ khi một số điều kiện đã được đáp ứng, chẳng hạn như cùng một Assembly được nạp vào cả hai cơ sở dữ liệu và một vài thứ khác (chi tiết trong câu hỏi trùng lặp nêu trên).

Đối với trường hợp cụ thể này, có cách để chuyển thông tin từ DB1 đến DB2, mặc dù đây không phải là giải pháp thanh lịch. Để sử dụng Kiểu Bảng, ngữ 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 trong đó Kiểu Bảng tồn tại. Điều này được thực hiện thông qua câu lệnh USE, nhưng điều đó chỉ có thể được thực hiện trong SQL động nếu cần phải đượ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, hoặc trong một nỗ lực để tránh những bảng tạm thời địa phương bằng cách đơn giản đi qua trong UDTT như một 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ự làm việc (mặc dù nó chắc chắn trông giống như nó nên). Có nghĩa là, 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ẽ thất bại trên "@ TableTypeDB2 có một kiểu dữ liệu hợp lệ", mặc dù nó đúng sẽ hiển thị rằng DB2 là "hiện tại" Cơ sở dữ liệu.Nó có một cái gì đó để làm với cách sp_executesql xác định kiểu dữ liệu biến kể từ khi lỗi được gọi là "#là" biến # 2 ", mặc dù nó được tạo cục bộ chứ không phải là tham số đầu vào.

Thực tế, sp_executesql sẽ báo lỗi nếu một biến duy nhất được khai báo (thông qua tham số nhập tham số danh sách sp_executesql), ngay cả khi nó không bao giờ được tham chiếu, hãy để một mình sử dụng. Có nghĩa là, các mã sau đây sẽ gặp phải lỗi tương tự không thể tìm định nghĩa cho UDTT điều đó xảy ra với các truy vấn ngay lập tức 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 

(Nhờ @ Mark Sowul đề cập đến rằng sp_executesql không làm việc khi đi qua các biến)

BAO GIỜ, vấn đề này có thể được giải quyết (tốt, miễn là bạn không cố gắng truyền vào 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 hiện của sp_executesql để quá trình này sẽ là cục bộ cho DB trong đó TVP kia tồn tại. Một điều tốt đẹp về sp_executesql là, không giống như EXEC, nó là một thủ tục lưu trữ, và một hệ thống lưu trữ thủ tục tại đó, do đó, nó có thể được hoàn toàn đủ điều kiện. Việc sử dụng thực tế này cho phép sp_executesql hoạt động, điều này cũng có nghĩa là không cần phải có câu lệnh USE [DB2]; trong SQL động. Mã sau đây không 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 
+1

Lưu ý rằng nếu bạn đang sử dụng 'sp_executesql', có vẻ như nó sẽ không hoạt động nếu bạn cố gắng sử dụng khối tham số –

+0

@MarkSowul Tôi đã kiểm tra và xem ý bạn là gì. Tôi đã cập nhật để thêm thông tin đó. Cảm ơn! –

+0

Không hoàn toàn - sử dụng bất kỳ tham số * nào vào 'sp_executesql' sẽ làm điều đó. Tôi vẫn đang sử dụng một bảng tạm thời; Tôi chỉ đơn giản là đi qua một cái gì đó vô hại khác. Sau đó, bạn sẽ nhận được các lỗi lạ * bên trong * SQL động, phàn nàn về việc sử dụng các loại bảng (mặc dù bạn đã chuyển dbs bằng 'USE') –

Các vấn đề liên quan