如果我有这样的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”;
我的问题是:
答案 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