您是否可以创建CLR UDT以允许跨数据库的共享表类型?

时间:2011-11-07 12:47:24

标签: sql-server sql-server-2008 tsql sqlclr user-defined-types

如果我有这样的SQL语句:

CREATE TYPE [dbo].[typeRateLimitVariables] AS TABLE(
            [vchColumnName] [varchar](250) NULL,
            [decColumnValue] [decimal](25, 10) NULL
)

我用它作为数据库中UDF的表变量,我有足够的范围。 BUt假设我想从同一服务器上的另一个数据库调用标量UDF,然后我最终会出现未知类型错误。

我尝试在调用DB上创建类型,但是obv。然后我得到一个类型不匹配,因为虽然每个UDT具有相同的名称,但它们具有不同的范围,因此是不同的类型。

我知道你可以创建CLR类型,将程序集注册到SQL Server,然后普遍访问自定义类型。

我的想法是创建一个类型为“TABLE”的CLR UDT,但我无法看到它是如何实现的,因为我知道它必须是CLR类型“SqlDbType.Structured”;

我的问题是:

  1. 有没有办法不使用CLR在SQL 2008 R2中为表变量创建全局范围,如果没有...
  2. 如何在C#CLR中定义UDT,其中UDT本质上是一个UDT“AS TABLE”

1 个答案:

答案 0 :(得分:4)

  

我知道你可以创建CLR类型,将程序集注册到SQL Server,   然后普遍访问自定义类型。

你确定吗?用户定义的类型是数据库级对象,而不是服务器级别的对象。 “普遍”访问它们的唯一方法是将程序集加载到每个数据库中,并在每个数据库中创建用户定义类型。 Registering User-Defined Types in SQL Server的MSDN文档中说明了这一点:

  

跨数据库使用UDT
  根据定义,UDT的范围限定为单个   数据库。因此,在一个数据库中定义的UDT不能用于   另一个数据库中的列定义。为了使用UDT   多个数据库,您必须执行CREATE ASSEMBLY和CREATE   相同程序集中每个数据库中的TYPE语句。大会   如果他们有相同的名字,强名,被认为是相同的,   文化,版本,权限集和二进制内容。

     

一旦UDT在两个数据库中注册并可访问,您就可以   从一个数据库转换UDT值以在另一个数据库中使用。相同   在以下情况下,可以跨数据库使用UDT:

     
      
  • 调用在不同数据库中定义的存储过程。
  •   
  • 查询在不同数据库中定义的表。
  •   
  • 从一个数据库表UDT列中选择UDT数据   将其插入具有相同UDT列的第二个数据库中。
  •   
     

在这些情况下,服务器所需的任何转换都会发生   自动。您无法明确执行转换   使用Transact-SQL CAST或CONVERT函数。

回答您的具体问题:

  

1)有没有一种方法可以不使用CLR在SQL 2008 R2中为表变量创建全局范围,如果没有...

表格类型和用户定义类型都不能跨数据库访问,在一个案例中接受CLR UDT,如上面MSDN文档中所述。

  

2)如何在C#CLR中定义UDT,其中UDT本质上是一个UDT“AS TABLE”

你不能因为它们是两个独立的东西(即“类型”与“表类型”)而不是仅仅是两种不同的实现方式(即T-SQL UDF /存储过程与SQLCLR UDF /存储过程)

修改

在纯技术层面上, 可以跨数据库使用类型(表类型和用户定义类型),但只能通过USE命令切换当前上下文。仅适用于ad hoc /动态SQL。因此,这种用法在实际水平上的适用性有限,但仍然可以通过以下示例显示:

SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET NOCOUNT ON;
GO

USE [msdb];
GO

PRINT 'Creating [GlobalTableDef] Table Type in [msdb]...';
CREATE TYPE dbo.GlobalTableDef
AS TABLE
(
    [ID] INT NOT NULL IDENTITY(17, 22),
    [CreateDate] DATETIME NOT NULL DEFAULT (GETDATE()),
    [Something] NVARCHAR(2000) NULL
);
GO

PRINT 'Creating [TotalBytes] Function in [msdb]...';
GO
CREATE FUNCTION dbo.TotalBytes
(
    @TableToSummarize dbo.GlobalTableDef READONLY
)
RETURNS INT
AS
BEGIN
    DECLARE @TotalBytes INT = 0;

SELECT  @TotalBytes += (4 + 8 + DATALENGTH(COALESCE(tmp.Something, '')))
    FROM    @TableToSummarize tmp;

    RETURN @TotalBytes;
END;
GO

PRINT 'Testing the Table Type and Function...';
DECLARE @TmpTable dbo.GlobalTableDef;
INSERT INTO @TmpTable (Something) VALUES (N'this is a test');
INSERT INTO @TmpTable (Something) VALUES (NULL);
INSERT INTO @TmpTable (Something) VALUES (N'still seems to be a test');

SELECT * FROM @TmpTable;

SELECT dbo.TotalBytes(@TmpTable) AS [TotalBytesUsed];
GO

USE [tempdb];
GO
PRINT 'Creating [TypeTest] Proc in [tempdb]...';
GO

CREATE PROCEDURE dbo.TypeTest
AS
SET NOCOUNT ON;

    SELECT 1 AS [Step], DB_NAME() AS [CurrentDB];

    EXEC('
        SELECT 2 AS [Step], DB_NAME() AS [CurrentDB];
        USE [msdb];
        SELECT 3 AS [Step], DB_NAME() AS [CurrentDB];
        DECLARE @TmpTable dbo.GlobalTableDef;
        USE [tempdb];
        SELECT 4 AS [Step], DB_NAME() AS [CurrentDB];

        -- local query to prove context is tempdb
        SELECT TOP 5 * FROM sys.objects;

        INSERT INTO @TmpTable (Something) VALUES (N''this is a new test'');
        INSERT INTO @TmpTable (Something) VALUES (NULL);
        INSERT INTO @TmpTable (Something) VALUES (N''non-empty value'');
        INSERT INTO @TmpTable (Something) VALUES (NULL);
        INSERT INTO @TmpTable (Something) VALUES (N''woo-hoo!!!!!!!!!!!!!!!'');
        SELECT * FROM @TmpTable;

        SELECT [msdb].dbo.TotalBytes(@TmpTable) AS [TotalBytesUsed];
    ');

GO

USE [master];
GO

SELECT 5 AS [Step], DB_NAME() AS [CurrentDB];
EXEC tempdb.dbo.TypeTest;

--------------------------------

USE [tempdb];
GO
IF (OBJECT_ID(N'tempdb.dbo.TypeTest') IS NOT NULL)
BEGIN
    PRINT 'Dropping [TypeTest] Proc from [tempdb]...';
    DROP PROCEDURE dbo.TypeTest;
END;
GO

USE [msdb];
GO
IF (OBJECT_ID(N'dbo.TotalBytes') IS NOT NULL)
BEGIN
    PRINT 'Dropping [TotalBytes] Function from [msdb]...';
    DROP FUNCTION dbo.TotalBytes;
END;
GO

IF (EXISTS(
        SELECT  *
        FROM    sys.table_types stt
        WHERE   stt.name = N'GlobalTableDef'
    ))
BEGIN
    PRINT 'Dropping [GlobalTableDef] Table Type from [msdb]...';
    DROP TYPE dbo.GlobalTableDef;
END;
GO