format () to niedeterministyczna wbudowana funkcja łańcucha… prawda?

10

Czy zanim opublikuję element dotyczący braku dokumentacji na ten temat, czy ktoś potwierdzi, że czegoś tu nie brakuje?

Na stronie dokumentów, gdzie formatjest wymieniony jako funkcja ciągu:

„Wszystkie wbudowane funkcje łańcuchowe są deterministyczne.” - Funkcje ciągów (Transact-SQL)

Nie ma też wzmianki o formatniedeterministyczności na powiązanych stronach:


Jednak podczas próby utworzenia utrwalonej kolumny obliczanej:

create table t (date_col date); 
insert into t values (getdate());
alter table t add date_formatted_01 as format(date_col,'YYYY') persisted;

Zwraca następujący błąd:

Obliczona kolumna „data_formatowana_01” w tabeli „t” nie może zostać utrwalona, ​​ponieważ kolumna nie jest deterministyczna.

Dokumentacja stwierdza, że

Jeśli argument kultury nie zostanie podany, zostanie użyty język bieżącej sesji.

ale dodanie argumentu dotyczącego kultury nic nie zmienia

To również się nie udaje

alter table t add date_formatted_02 as format(date_col, 'd', 'en-US' ) persisted

demo rextestera: http://rextester.com/ZMS22966

demo dbfiddle.uk: http://dbfiddle.uk/?rdbms=sqlserver_next&fiddle=7fc57d1916e901cb561b551af144aed6

SqlZim
źródło
1
To również nie: alter table #t add date_formatted_01 as CONVERT(VARCHAR(20), FORMAT(date_col, 'YYYY', 'en-US')) persisted;. Nie jestem pewien, dlaczego FORMATnie jest deterministyczny, szczególnie przy określaniu kultury. date_formattedKolumna może być VARCHAR(20)(jeszcze utrzymywały) i ustawiona za pomocą wyzwalacza FORMAT. Lub działa SQLCLR. Korzystając z biblioteki SQL # SQLCLR (którą napisałem), możesz to zrobić ALTER TABLE SQL#.t ADD date_formatted_03 AS SQL#.Date_Format(date_col, 'd', 'en-US') PERSISTED;(tabela jest własnością SQL #, ponieważ właściciel tabeli i funkcji musi być taki sam).
Solomon Rutzky

Odpowiedzi:

5

Funkcja niekoniecznie musi być deterministyczna lub niedeterministyczna. Istnieje kilka funkcji, które mogą być deterministyczne w zależności od sposobu ich użycia :

Poniższe funkcje nie zawsze są deterministyczne, ale można ich używać w widokach indeksowanych lub indeksach w kolumnach obliczeniowych, jeśli są one określone w sposób deterministyczny.

CASTi CONVERTsą takimi przykładami. Bazując na testach, które przeprowadziłeś do tej pory, myślę, że można śmiało powiedzieć, że FORMATnie zawsze jest to deterministyczne, mimo że jest funkcją strunową. Jeśli chcesz wiedzieć, czy czasami jest to deterministyczne, jedyną techniką, jaką mogę wymyślić, jest wypróbowanie wystarczająco wielu różnych sposobów nazywania go, dopóki nie będziesz zadowolony. Rozważmy FORMATna przykład, w odniesieniu do liczb. Istnieje tylko dziesięć różnych typów wprowadzania numerycznego :

typy wprowadzania numerycznego

Wydaje się również, że istnieje tylko dziewięć różnych formatów numerycznych . Można spróbować utworzyć trwałe kolumny dla wszystkich możliwych kombinacji. Poniżej znajduje się kod do zrobienia tego:

DECLARE @FormatValue INT = 76767; -- change this if you want
DECLARE @FormatCulture VARCHAR(10) = 'en-US'; -- change this if you want
DECLARE @Format VARCHAR(1);
DECLARE @FormatType VARCHAR(10);
DECLARE @SQLForColumn VARCHAR(200);
DECLARE @TestNumber INT = 0;

BEGIN

    DROP TABLE IF EXISTS dbo.TargetTable;
    CREATE TABLE dbo.TargetTable (ID INT);

    DROP TABLE IF EXISTS #ColumnAddResults;
    CREATE TABLE #ColumnAddResults (
    FormatType VARCHAR(10),
    [Format] VARCHAR(1), 
    Succeeded VARCHAR(1), 
    ErrorMessage VARCHAR(1000)
    );

    drop table if exists #Types;
    create table #Types (FormatType VARCHAR(10));

    INSERT INTO #Types VALUES
    ('bigint'), ('int'), ('smallint'), ('tinyint'), ('decimal')
    , ('numeric'), ('float'), ('real'), ('smallmoney'), ('money');

    drop table if exists #Formats;
    create table #Formats ([Format] VARCHAR(1));

    INSERT INTO #Formats VALUES 
    ('C'), ('D'), ('E'), ('F'), ('G'), ('N'), ('P'), ('R'), ('X');

    DECLARE format_statements CURSOR LOCAL FAST_FORWARD FOR 
    SELECT #Types.FormatType, #Formats.[Format]
    FROM #Formats
    CROSS JOIN #Types;

    OPEN format_statements;

    FETCH NEXT FROM format_statements   
    INTO @FormatType, @Format;  

    WHILE @@FETCH_STATUS = 0  
    BEGIN
        SET @TestNumber = @TestNumber + 1;
        SET @SQLForColumn = 'alter table dbo.TargetTable add NewColumn' + CAST(@TestNumber AS VARCHAR(10))
        + ' as FORMAT(CAST(' +  CAST(@FormatValue AS VARCHAR(10)) + ' AS ' + @FormatType + '), '
        + '''' + @Format + ''', ''' + @FormatCulture + ''') persisted';

        BEGIN TRY
            EXEC (@SQLForColumn);
            INSERT INTO #ColumnAddResults VALUES (@FormatType, @Format, 'Y', NULL);
        END TRY
        BEGIN CATCH
            INSERT INTO #ColumnAddResults VALUES (@FormatType, @Format, 'N', ERROR_MESSAGE());
        END CATCH;

        PRINT @SQLForColumn;

        FETCH NEXT FROM format_statements   
        INTO @FormatType, @Format;  
    END;

    CLOSE format_statements;  
    DEALLOCATE format_statements;  

    SELECT * FROM dbo.TargetTable;
    SELECT * FROM #ColumnAddResults;
    DROP TABLE #ColumnAddResults;

END;

Oto próbka danych wyjściowych:

kod wyjściowy testu

Nie byłem w stanie uzyskać żadnej kolumny, która mogłaby zostać dodana do tabeli dla kilku wartości wejściowych i kultur. Nie wypróbowałem wyczerpująco wszystkich możliwych kultur, ponieważ nie mogę znaleźć ich listy w SQL Server.

FORMATPrzynajmniej wydaje się bezpiecznie stwierdzić, że dokumentacja dotycząca determinizmu jest niepoprawna, dlatego zaleciłbym przesłanie w tym celu elementu Connect.

Joe Obbish
źródło
1

Nie jestem zwykłym użytkownikiem sqlserver, więc mogę się mylić, ale domyślam się, że format nie jest funkcją łańcuchową. Zgodnie z dokumentacją:

https://docs.microsoft.com/en-us/sql/t-sql/functions/format-transact-sql

Format przyjmuje jako argument typ daty lub typ liczbowy. Jeśli wszystko, co chcesz zrobić, to złapać część roku na randkę, czy nie możesz użyć funkcji roku?

alter table t 
    add date_formatted_01 as year(date_col) persisted;

jeśli chcesz reprezentację ciągu:

alter table t 
    add date_formatted_01 as cast(year(date_col) as char(4)) persisted;
Lennart
źródło
1
Jest wymieniony jako funkcja ciągu w dokumentach. i.stack.imgur.com/aj0T2.png
Martin Smith
1
@MartinSmith, ciekawe. Osobiście uważam, że jest to sprzeczne z intuicją i staje się logicznie niespójne z „Wszystkie wbudowane funkcje łańcuchowe są deterministyczne”.
Lennart
@ Lennart Doceniam alternatywę dla przykładu, ale przykład nie był ważny.
SqlZim
1
@SqlZim, pomyślałem, że twój przykład jest tylko przykładem, ale dodałem alternatywę na wszelki wypadek. Nie jestem pewien, jakie dokładnie jest twoje pytanie, czy chodzi o to, czy format jest funkcją łańcucha, czy nie, czy jest deterministyczny czy nie, czy też wszystko to jest źle udokumentowane?
Lennart