SSD中的SQLCLR Nullable DATETIME2

时间:2012-12-21 15:36:41

标签: sql-server sqlclr sql-server-data-tools

我想写一个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的全范围和精确度。

2 个答案:

答案 0 :(得分:0)

看看this。 SqlDateTime允许空值,因此能够通过类'属性检查值是否为空。

答案 1 :(得分:0)

问题在于SSDT(SQL Server数据工具)而不是SQLCLR。是的,SQLCLR确实通过DATETIME2 / DateTime?支持Nullable<DateTime>。不幸的是,SSDT(我正在使用VS2013和SSDT v 12.0.50512.0)尚未(我在这里很乐观,我知道)支持从DATETIME2推断DateTimeDateTime?。但是,使用DateTime?时也不会出现错误。尽管如此,DateTimeDateTime?仍会在生成的SQL中显示为常规DATETIME

我不确定是否有任何真正“适当”的方法告诉SSDT数据类型应该是什么。实际上有很多不受支持的选项,包括WITH RETURNS NULL ON NULL INPUT等常规UDF选项和默认值等参数选项。这是令人伤心和令人沮丧的,是的。

到目前为止我提出的最好的(我还在研究其他选项)是将{Post 1>部署脚本添加到ALTER函数的定义中,并带有所需的选项:

  1. PROJECT 菜单中,选择添加新项... 控制 + Shift + VS2013中的
  2. 转到 SQL Server - &gt; 用户脚本
  3. 选择部署后脚本
  4. 给它命名(名称本身不确定它是预部署,后部署还是两者都没有;它只需要以 .sql 结尾)并单击添加
  5. 您将被置于(大多数)空SQL脚本
  6. 输入一个或多个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
    
  7. 此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属性中。