format () adalah fungsi string bawaan nondeterministic ... kan?

10

Sebelum saya mengirim item sambung tentang kurangnya dokumentasi tentang hal ini, apakah seseorang akan mengonfirmasi bahwa saya tidak hanya melewatkan sesuatu di sini?

Pada halaman dokumen tempat formatterdaftar sebagai fungsi string:

"Semua fungsi string bawaan bersifat deterministik." - Fungsi String (Transact-SQL)

Juga tidak disebutkan tidak formatbersifat deterministik pada halaman terkait:


Namun, saat mencoba membuat kolom yang dihitung tetap:

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

Mengembalikan kesalahan berikut:

Kolom yang dihitung 'date_formatted_01' pada tabel 't' tidak dapat dipertahankan karena kolom tersebut non-deterministik.

Dokumentasi menyatakan itu

Jika argumen budaya tidak disediakan, bahasa sesi saat ini digunakan.

tetapi menambahkan argumen budaya tidak mengubah banyak hal

Ini juga gagal

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

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

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

SqlZim
sumber
1
Ini juga gagal: alter table #t add date_formatted_01 as CONVERT(VARCHAR(20), FORMAT(date_col, 'YYYY', 'en-US')) persisted;. Tidak yakin mengapa FORMATtidak deterministik, terutama saat menentukan budaya. The date_formattedkolom dapat dilakukan VARCHAR(20)(masih bertahan) dan diatur melalui Pemicu menggunakan FORMAT. Atau SQLCLR berfungsi. Menggunakan perpustakaan SQL # SQLCLR (yang saya tulis), Anda dapat melakukannya ALTER TABLE SQL#.t ADD date_formatted_03 AS SQL#.Date_Format(date_col, 'd', 'en-US') PERSISTED;(tabel dimiliki oleh SQL # karena pemilik tabel dan fungsi harus sama).
Solomon Rutzky

Jawaban:

5

Suatu fungsi belum tentu deterministik atau nondeterministik. Ada beberapa fungsi yang dapat ditentukan tergantung pada bagaimana mereka digunakan :

Fungsi-fungsi berikut ini tidak selalu bersifat deterministik, tetapi dapat digunakan dalam tampilan yang diindeks atau indeks pada kolom yang dihitung saat mereka ditentukan secara deterministik.

CASTdan CONVERTmerupakan contoh seperti itu. Berdasarkan pengujian yang telah Anda lakukan sejauh ini, saya pikir itu adil untuk mengatakan bahwa FORMATtidak selalu deterministik, meskipun merupakan fungsi string. Jika Anda ingin tahu apakah kadang-kadang deterministik, satu-satunya teknik yang dapat saya pikirkan adalah mencoba berbagai cara untuk menyebutnya sampai Anda puas. Sebagai contoh, mari kita pertimbangkan FORMATsebagaimana diterapkan pada angka. Hanya ada sepuluh jenis input numerik yang berbeda :

tipe input numerik

Tampaknya juga hanya ada sembilan format angka yang berbeda . Dimungkinkan untuk mencoba membuat kolom tetap untuk semua kemungkinan kombinasi. Beberapa kode untuk melakukannya adalah di bawah ini:

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;

Berikut contoh hasilnya:

output kode uji

Saya tidak bisa mendapatkan salah satu kolom yang akan ditambahkan ke tabel untuk beberapa nilai input dan budaya. Saya tidak mencoba semua budaya yang mungkin karena saya tidak dapat menemukan daftar mereka dalam SQL Server.

Setidaknya sepertinya aman untuk menyimpulkan bahwa dokumentasi mengenai determinisme FORMATtidak benar, jadi saya akan merekomendasikan mengirimkan item terhubung untuk itu.

Joe Obbish
sumber
1

Saya bukan pengguna sqlserver biasa, jadi saya mungkin salah, tapi dugaan saya adalah format itu bukan fungsi string. Menurut dokumentasi:

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

Format mengambil tipe tanggal atau tipe numerik sebagai argumen. Jika yang ingin Anda lakukan hanyalah mengambil bagian tahun dari tanggal, tidak bisakah Anda menggunakan fungsi tahun?

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

jika Anda ingin representasi string:

alter table t 
    add date_formatted_01 as cast(year(date_col) as char(4)) persisted;
Lennart
sumber
1
Ini terdaftar sebagai fungsi string dalam dokumen. i.stack.imgur.com/aj0T2.png
Martin Smith
1
@ MartinSmith, menarik. Secara pribadi saya menemukannya kontra intuitif dan juga menjadi tidak konsisten secara logis dengan "Semua fungsi string bawaan bersifat deterministik."
Lennart
@Lennart Saya menghargai alternatif untuk contoh ini, tetapi contoh itu tidak penting.
SqlZim
1
@ SqlZim, saya pikir contoh Anda hanya contoh tapi saya menambahkan alternatif untuk berjaga-jaga. Saya tidak yakin apa pertanyaan Anda sebenarnya, apakah itu format fungsi string atau tidak, apakah itu deterministik atau tidak, atau apakah semua itu didokumentasikan dengan buruk?
Lennart