format () è una funzione di stringa incorporata non deterministica ... giusto?


10

Prima di pubblicare un elemento di connessione relativo alla mancanza di documentazione in merito, qualcuno confermerà che non mi sto semplicemente perdendo qualcosa qui?

Nella pagina dei documenti in cui formatè elencata come una funzione stringa:

"Tutte le funzioni di stringa integrate sono deterministiche." - Funzioni stringa (Transact-SQL)

Non si fa menzione di formatnon determinismo nelle pagine correlate:


Tuttavia, quando si tenta di creare una colonna calcolata persistente:

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

Restituisce il seguente errore:

La colonna calcolata 'date_formatted_01' nella tabella 't' non può essere mantenuta perché la colonna non è deterministica.

La documentazione afferma che

Se l'argomento cultura non viene fornito, viene utilizzata la lingua della sessione corrente.

ma l'aggiunta di un argomento culturale non cambia le cose

Anche questo fallisce

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

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

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


1
Questo fallisce anche: alter table #t add date_formatted_01 as CONVERT(VARCHAR(20), FORMAT(date_col, 'YYYY', 'en-US')) persisted;. Non so perché FORMATnon sia deterministico, soprattutto quando si specifica la cultura. La date_formattedcolonna può essere VARCHAR(20)(ancora persistente) e impostare tramite Trigger utilizzando FORMAT. O funziona SQLCLR. Usando la libreria SQL # SQLCLR (che ho scritto), puoi farlo ALTER TABLE SQL#.t ADD date_formatted_03 AS SQL#.Date_Format(date_col, 'd', 'en-US') PERSISTED;(la tabella è di proprietà di SQL # poiché il proprietario della tabella e della funzione deve essere lo stesso).
Solomon Rutzky,

Risposte:


5

Una funzione non è necessariamente deterministica o non deterministica. Ci sono alcune funzioni che possono essere deterministiche a seconda di come vengono utilizzate :

Le seguenti funzioni non sono sempre deterministiche, ma possono essere utilizzate in viste o indici indicizzati su colonne calcolate quando sono specificate in modo deterministico.

CASTe CONVERTsono tali esempi. Sulla base dei test che hai svolto finora, penso che sia giusto dire che FORMATnon è sempre deterministico, nonostante sia una funzione stringa. Se vuoi sapere se a volte è deterministico, l'unica tecnica a cui riesco a pensare è di provare abbastanza modi diversi per chiamarlo fino a quando non sei soddisfatto. Ad esempio, consideriamo FORMATapplicato ai numeri. Esistono solo dieci diversi tipi di input numerici :

tipi di input numerici

Sembra inoltre che ci siano solo nove diversi formati numerici . È possibile provare a creare colonne persistenti per tutte le possibili combinazioni. Di seguito è riportato un codice per farlo:

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;

Ecco un esempio dell'output:

uscita del codice di prova

Non sono stato in grado di ottenere nessuna delle colonne da aggiungere alla tabella per alcuni valori di input e culture. Non ho provato esaustivamente tutte le culture possibili perché non riesco a trovarne un elenco in SQL Server.

Almeno sembra sicuro concludere che la documentazione relativa al determinismo di FORMATnon è corretta, quindi consiglierei di inviare un elemento di connessione per esso.



1

Non sono un normale utente di sqlserver, quindi potrei sbagliarmi, ma la mia ipotesi è che il formato non sia una funzione stringa. Secondo la documentazione:

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

Il formato accetta un tipo di data o un tipo numerico come argomento. Se tutto quello che vuoi fare è prendere la parte dell'anno di una data, non puoi usare la funzione anno?

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

se si desidera una rappresentazione di stringa:

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

1
È elencato come una funzione di stringa nei documenti. i.stack.imgur.com/aj0T2.png
Martin Smith,

1
@MartinSmith, interessante. Personalmente lo trovo contro intuitivo e diventa anche logicamente incoerente con "Tutte le funzioni di stringa incorporate sono deterministiche".
Lennart,

@Lennart Apprezzo l'alternativa all'esempio, ma l'esempio non è stato importante.
SqlZim,

1
@SqlZim, ho pensato che il tuo esempio fosse solo un esempio, ma ho aggiunto un'alternativa per ogni evenienza. Non sono sicuro di quale sia esattamente la tua domanda, è se il formato è una funzione stringa o no, se è deterministico o no, o se tutto ciò è mal documentato?
Lennart,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.