我想写一个SQLCLR UDF,它接受DATETIME2
并返回DATETIME2
。
输入和输出应该允许NULL。
我创建一个SQL Server数据库项目(SSDT),在其SQLCLR属性中将其配置为VB语言,然后添加以下文件Test.vb
:
Option Explicit On
Option Strict On
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Partial Public Class UserDefinedFunctions
<SqlFunction()> _
Public Shared Function Test(d As Nullable(Of DateTime)) As Nullable(Of DateTime)
Return d
End Function
End Class
以这种方式使用nullable似乎从SQL Server 2008开始每http://msdn.microsoft.com/en-us/library/ms131092(v=SQL.100).aspx支持。
但是,当我运行deploy命令时,出现以下错误:
SQL46010:附近的语法不正确。
这是因为它生成的SQL是:
CREATE FUNCTION [dbo].[Test] (@d /* Error: Unsupported type. */)
RETURNS /* Error: Unsupported type. */
AS EXTERNAL NAME [Test].[Test.UserDefinedFunctions].[Test];
我无法替换SqlDateTime
,因为我需要DATETIME2
的全范围和精确度。
答案 0 :(得分:0)
看看this。 SqlDateTime允许空值,因此能够通过类'属性检查值是否为空。
答案 1 :(得分:0)
问题在于SSDT(SQL Server数据工具)而不是SQLCLR。是的,SQLCLR确实通过DATETIME2
/ DateTime?
支持Nullable<DateTime>
。不幸的是,SSDT(我正在使用VS2013和SSDT v 12.0.50512.0)尚未(我在这里很乐观,我知道)支持从DATETIME2
推断DateTime
或DateTime?
。但是,使用DateTime?
时也不会出现错误。尽管如此,DateTime
或DateTime?
仍会在生成的SQL中显示为常规DATETIME
。
我不确定是否有任何真正“适当”的方法告诉SSDT数据类型应该是什么。实际上有很多不受支持的选项,包括WITH RETURNS NULL ON NULL INPUT
等常规UDF选项和默认值等参数选项。这是令人伤心和令人沮丧的,是的。
到目前为止我提出的最好的(我还在研究其他选项)是将{Post 1>部署脚本添加到ALTER
函数的定义中,并带有所需的选项:
输入一个或多个ALTER
语句,类似于以下内容:
-- declare once
DECLARE @ObjectName sysname; -- keep lower-case to work in case-sensitive collations
SET @ObjectName = N'Test';
IF (EXISTS(
SELECT *
FROM sys.assembly_modules sam
WHERE sam.[object_id] = OBJECT_ID(@ObjectName)
)
)
BEGIN
PRINT 'Checking custom properties for [' + @ObjectName + N']...';
IF (EXISTS(
SELECT *
FROM sys.parameters sp
INNER JOIN sys.types st
ON st.system_type_id = sp.system_type_id
WHERE sp.[object_id] = OBJECT_ID(@ObjectName)
AND st.[name] <> N'datetime2' -- keep lower-case to work in
-- case-sensitive collations
)
)
BEGIN
PRINT 'Setting custom properties for [' + @ObjectName + N']...';
BEGIN TRY
EXEC('
ALTER FUNCTION [dbo].[Test](@d [datetime2])
RETURNS [datetime2] WITH EXECUTE AS CALLER
AS EXTERNAL NAME [Test].[Test.UserDefinedFunctions].[Test];
');
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
SET @ErrorMessage = ERROR_MESSAGE();
RAISERROR(@ErrorMessage, 16, 1);
RETURN;
END CATCH;
END;
END;
ELSE
BEGIN
RAISERROR(N'Oops. [%s] was renamed or no longer exists!', 16, 1, @ObjectName);
RETURN;
END;
---
SET @ObjectName = N'NextObjectToFix';
-- copy the rest from above
此Post Deployment脚本将始终包含在_Create
和发布/增量构建脚本的末尾。因此,额外的逻辑是看是否已经存在变化。是的,一般并不会总是运行ALTER
,但在极少数情况下,此对象是其他内容的依赖项,例如Check Constraint或Computed Column,它是除非需要改变,否则最好不要管它。
您可以从ALTER
脚本(只需将\bin\Configuration\*_Create.sql
更改为CREATE
)获得正确的ALTER
定义,如果右键单击,则可以在SSMS中获得正确的CREATE
定义对象并选择修改。在这两种情况下,更改数据类型和任何其他选项(显然; - )。
另一个有点相关的想法是,不依赖于Visual Studio / SSDT发布过程来处理T-SQL包装器对象CREATE FUNCTION ...
语句,而只是使用它来管理程序集。在此设置中,您将取消 - 检查项目属性的 SQLCLR 标签上的生成DDL 选项。然后,您将添加一个Post Deployment脚本(如上面的建议中所述)并添加您自己的CREATE PROCEDURE ...
,CREATE
等语句。
对于初始开发而言,引入新对象并不像使用SSDT生成DDL那样快速和简单,但考虑到新对象不是经常创建的,并且他们的签名更改频率低得多,管理这个DDL自己真的不是那么糟糕(事实上,它与我用于我的SQL#库的过程非常相似,它有超过250个对象。实际上,一旦你有一个对象类型{{ 1}}语句,你可以只复制并粘贴它们用于新对象并更改名称和参数等。这种方法所需的大多数工作是在创建一个新的TVF时,如果它返回一堆字段,但这不是如果您使用SSDT来管理DDL创建,那么真正为您提供的大量工作无论如何都必须将其输入到TableDefinition
属性的SqlFunction
属性中。