format () ist eine nicht deterministische integrierte Zeichenfolgenfunktion… richtig?

10

Wird jemand bestätigen, dass mir hier nicht einfach etwas fehlt, bevor ich ein Verbindungselement bezüglich des Mangels an Dokumentation dazu poste?

Auf der Dokumentenseite, auf der formateine Zeichenfolgenfunktion aufgeführt ist:

"Alle integrierten Zeichenfolgenfunktionen sind deterministisch." - String-Funktionen (Transact-SQL)

Es wird auch nicht erwähnt, formatdass es auf verwandten Seiten nicht deterministisch ist:


Wenn Sie jedoch versuchen, eine dauerhaft berechnete Spalte zu erstellen:

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

Gibt den folgenden Fehler zurück:

Die berechnete Spalte 'date_formatted_01' in der Tabelle 't' kann nicht beibehalten werden, da die Spalte nicht deterministisch ist.

Die Dokumentation besagt, dass

Wenn das Kulturargument nicht angegeben wird, wird die Sprache der aktuellen Sitzung verwendet.

Das Hinzufügen eines Kulturarguments ändert jedoch nichts

Dies schlägt ebenfalls fehl

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

Rextester-Demo: http://rextester.com/ZMS22966

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

SqlZim
quelle
1
Dies schlägt auch fehl : alter table #t add date_formatted_01 as CONVERT(VARCHAR(20), FORMAT(date_col, 'YYYY', 'en-US')) persisted;. Ich bin mir nicht sicher, warum FORMATdas nicht deterministisch ist, besonders wenn ich die Kultur spezifiziere. Die date_formattedSpalte kann sein VARCHAR(20)(noch anhielt) und stellen Sie über Trigger FORMAT. Oder SQLCLR funktioniert. Mit der SQL # SQLCLR-Bibliothek (die ich geschrieben habe) können Sie dies tun ALTER TABLE SQL#.t ADD date_formatted_03 AS SQL#.Date_Format(date_col, 'd', 'en-US') PERSISTED;(Tabelle gehört SQL #, da Tabelle und Funktionseigentümer identisch sein müssen).
Solomon Rutzky

Antworten:

5

Eine Funktion ist nicht unbedingt deterministisch oder nicht deterministisch. Es gibt einige Funktionen, die je nach Verwendung deterministisch sein können :

Die folgenden Funktionen sind nicht immer deterministisch, können jedoch in indizierten Ansichten oder Indizes für berechnete Spalten verwendet werden, wenn sie deterministisch angegeben werden.

CASTund CONVERTsind solche Beispiele. Basierend auf den Tests, die Sie bisher durchgeführt haben, denke ich, dass es fair ist zu sagen, dass dies FORMATnicht immer deterministisch ist, obwohl es sich um eine Zeichenfolgenfunktion handelt. Wenn Sie wissen möchten, ob es manchmal deterministisch ist, kann ich mir nur vorstellen, genug verschiedene Methoden auszuprobieren, um es zu nennen, bis Sie zufrieden sind. Betrachten wir zum Beispiel, FORMATwie es auf Zahlen angewendet wird. Es gibt nur zehn verschiedene numerische Eingabetypen :

numerische Eingabetypen

Es scheint auch nur neun verschiedene numerische Formate zu geben . Es ist möglich, dauerhafte Spalten für alle möglichen Kombinationen zu erstellen. Ein Code dafür ist unten:

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;

Hier ist ein Beispiel für die Ausgabe:

Testcode-Ausgabe

Ich konnte keine der Spalten für einige Eingabewerte und Kulturen zur Tabelle hinzufügen. Ich habe nicht alle möglichen Kulturen ausführlich ausprobiert, da ich in SQL Server keine Liste finden kann.

Zumindest scheint es sicher zu sein, dass die Dokumentation bezüglich des Determinismus von FORMATfalsch ist, daher würde ich empfehlen, ein Verbindungselement dafür einzureichen.

Joe Obbish
quelle
1

Ich bin kein regulärer Benutzer von sqlserver, daher könnte ich mich irren, aber ich vermute, dass das Format keine Zeichenfolgenfunktion ist. Laut Dokumentation:

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

Das Format verwendet entweder einen Datumstyp oder einen numerischen Typ als Argument. Wenn Sie nur den Jahresteil eines Datums erfassen möchten, können Sie die Jahresfunktion nicht verwenden?

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

Wenn Sie eine Zeichenfolgendarstellung wünschen:

alter table t 
    add date_formatted_01 as cast(year(date_col) as char(4)) persisted;
Lennart
quelle
1
Es ist als Zeichenfolgenfunktion in den Dokumenten aufgeführt. i.stack.imgur.com/aj0T2.png
Martin Smith
1
@ MartinSmith, interessant. Persönlich finde ich es kontraintuitiv und es wird auch logisch inkonsistent mit "Alle eingebauten String-Funktionen sind deterministisch."
Lennart
@ Lennart Ich schätze die Alternative zum Beispiel, aber das Beispiel war nicht wichtig.
SqlZim
1
@SqlZim, ich habe gedacht, dass Ihr Beispiel nur ein Beispiel ist, aber ich habe für alle Fälle eine Alternative hinzugefügt. Ich bin mir nicht sicher, was Ihre Frage genau ist, ob das Format eine Zeichenfolgenfunktion ist oder nicht, ob es deterministisch ist oder nicht oder ob all dies schlecht dokumentiert ist.
Lennart