SQLCLR Nullable DATETIME2 в SSDT

Я хочу написать пользовательскую функцию SQLCLR, которая принимает DATETIME2 и возвращает DATETIME2. Ввод и вывод должны разрешать NULL.

Я создаю проект базы данных SQL Server (SSDT), настраиваю его как язык VB в свойствах SQLCLR, а затем добавляю следующий файл 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.

Однако, когда я запускаю команду развертывания, я получаю следующую ошибку:

SQL46010: Неверный синтаксис рядом с ).

Это потому, что сгенерированный SQL был:

CREATE FUNCTION [dbo].[Test] (@d /* Error: Unsupported type. */)
RETURNS /* Error: Unsupported type. */
    AS EXTERNAL NAME [Test].[Test.UserDefinedFunctions].[Test];

Я не могу заменить SqlDateTime, потому что мне нужен полный диапазон и точность DATETIME2.


person Jason Kresowaty    schedule 21.12.2012    source источник
comment
Я считаю, что SQLDateTime имеет более высокое разрешение в новых версиях .NET. Вы пробовали это?   -  person Sebastian Meine    schedule 21.12.2012
comment
Извините, мой комментарий выше не соответствует действительности: msdn.microsoft.com/en-us/library/   -  person Sebastian Meine    schedule 21.12.2012
comment
Джейсон: Я понимаю, что прошло уже несколько лет, но на случай, если вы все еще ищете помощи в этом, я только что опубликовал ответ :-).   -  person Solomon Rutzky    schedule 18.08.2015


Ответы (2)


Взгляните на это. SqlDateTime допускает нулевое значение, следовательно, возможность проверить, является ли значение нулевым через свойство класса.

person Michael Ritchson    schedule 26.12.2012
comment
Но SqlDateTime сопоставляется с менее точным типом datetime в SQL Server, поэтому это неприемлемо. Для сопоставления с более точным типом «datetime2» в SQL Server ваши функции CLR должны иметь тип CLR DateTime, который не допускает пустых значений... поэтому нам действительно нужно иметь возможность использовать Nullable‹DateTime›. Если это невозможно, то есть серьезная проблема. - person Triynko; 27.02.2013
comment
@Triyko Это могло быть невозможно несколько версий назад для Visual Studio, но это позволяет вам (я думаю, по крайней мере, с VS2012) использовать DateTime?. Однако проблема заключается в том, что процесс публикации SSDT по-прежнему переводит его в DATETIME на стороне T-SQL. Я предложил два обходных пути в своем ответе. - person Solomon Rutzky; 21.08.2015

Проблема связана с SSDT (инструменты данных SQL Server), а не с SQLCLR. Да, SQLCLR действительно поддерживает DATETIME2 через DateTime?/Nullable<DateTime>. К сожалению, SSDT (я использую VS2013 и SSDT версии 12.0.50512.0) пока (знаю, я настроен оптимистично) не поддерживает вывод DATETIME2 из DateTime или DateTime?. Но также не возникает ошибок при использовании DateTime?, как раньше. Тем не менее, либо DateTime, либо DateTime? будут отображаться как обычные DATETIME в сгенерированном SQL.

Я не уверен в каких-либо действительно «подходящих» способах сообщить SSDT, каким должен быть тип данных. На самом деле существует довольно много неподдерживаемых параметров, включая общие параметры UDF, такие как WITH RETURNS NULL ON NULL INPUT, и параметры параметров, такие как значения по умолчанию. Грустно и обидно, да.

Лучшее, что я придумал до сих пор (и я все еще ищу другие варианты), — это добавить скрипт Post Deployment в ALTER определение функции с желаемыми параметрами:

  1. В меню ПРОЕКТ выберите Добавить новый элемент... (Control+Shift+A< /kbd> в VS2013)
  2. Перейдите в раздел SQL Server > Пользовательские сценарии.
  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
    

Этот сценарий после развертывания будет всегда включаться в конец сценариев _Create и публикации/добавочной сборки. Отсюда дополнительная логика, чтобы увидеть, присутствуют ли уже изменения или нет. Правда, обычно не помешает всегда запускать ALTER, но в тех редких случаях, когда этот объект является зависимостью от чего-то другого, например, контрольного ограничения или вычисляемого столбца, вероятно, лучше просто оставьте его в покое, если он не нуждается в изменении.

Правильное определение ALTER можно получить либо из сценария \bin\Configuration\*_Create.sql (просто измените CREATE на ALTER), либо в SSMS, если щелкнуть объект правой кнопкой мыши и выбрать Изменить. В обоих случаях измените тип данных и любые другие параметры (очевидно ;-).


Другая, отчасти связанная идея состоит в том, чтобы просто не полагаться на процесс публикации Visual Studio/SSDT для операторов объекта-оболочки T-SQL CREATE, а использовать его только для управления сборкой. В этой настройке вы должны снять флажок Создать DDL на вкладке SQLCLR в Свойствах проекта. Затем вы должны добавить сценарий после развертывания (как указано в предложении выше) и добавить свои собственные операторы CREATE FUNCTION ..., CREATE PROCEDURE ... и т. д.

Для первоначальной разработки это не так быстро и просто для введения нового объекта, как использование SSDT для создания этого DDL, но, учитывая, что новые объекты создаются не так уж часто и их сигнатуры меняются гораздо реже, управлять этим DDL самостоятельно действительно реально. не так уж и плох (и на самом деле он очень похож на процесс, который я использую для моей библиотеки SQL#, в которой более 250 объектов. С практической точки зрения, когда у вас есть по одному оператору CREATE каждого типа объекта, вы можете просто скопировать и вставить их для новых объектов, изменить имена, параметры и т. д. Больше всего работы, которую когда-либо требует этот подход, — это создание нового TVF если он возвращает кучу полей, но на самом деле это не так уж много работы, учитывая, что вам все равно пришлось бы вводить то же самое в свойство TableDefinition атрибута SqlFunction, если бы вы использовали SSDT для управления созданием DDL.

person Solomon Rutzky    schedule 18.08.2015