format()は非決定的な組み込みの文字列関数です…


10

これに関するドキュメントの欠如に関する接続項目を投稿する前に、誰かが私がここで単に何かを見落としているのではないことを確認しますか?

format文字列関数としてリストされているドキュメントページで:

「すべての組み込み文字列関数は確定的です。」- 文字列関数(Transact-SQL)

format関連ページで非決定的であることについての言及もありません:


ただし、永続的な計算列を作成しようとすると、次のようになります。

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

次のエラーを返します。

列が非決定的であるため、テーブル 't'の計算された列 'date_formatted_01'は永続化できません。

ドキュメントには、

カルチャー引数が指定されていない場合、現在のセッションの言語が使用されます。

しかし、文化の議論を追加しても、状況は変わりません

これも失敗します

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

rextesterデモ:http ://rextester.com/ZMS22966

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


1
これも失敗します:alter table #t add date_formatted_01 as CONVERT(VARCHAR(20), FORMAT(date_col, 'YYYY', 'en-US')) persisted;FORMAT特に文化を指定する場合、なぜが確定的ではないのかわかりません。date_formattedカラムをすることができることがVARCHAR(20)(依然として持続)と使用してトリガーを介して設定されましたFORMAT。または、SQLCLRが機能します。(私が書いた)SQL# SQLCLRライブラリーを使用すると、これを行うことができますALTER TABLE SQL#.t ADD date_formatted_03 AS SQL#.Date_Format(date_col, 'd', 'en-US') PERSISTED;(テーブルと関数の所有者は同じである必要があるため、テーブルはSQL#によって所有されます)。
ソロモンルツキー

回答:


5

関数は必ずしも確定的または非確定的ではありません。それらがどのように使用されるかによって決定論的になることができるいくつかの関数があります:

次の関数は常に確定的ではありませんが、確定的方法で指定されている場合、インデックス付きビューまたは計算列のインデックスで使用できます。

CASTCONVERTそのような例です。これまでに行ったテストに基づいFORMATて、文字列関数であるにもかかわらず、それが常に確定的であるとは限らないと言えます。あなたがそれが時々決定論的であるかどうか知りたいなら、私が考えることができる唯一のテクニックはあなたが満足するまでそれを呼び出すのに十分な異なる方法を試すことです。たとえば、FORMAT数値に適用した場合を考えてみましょう。数値入力タイプは 10 種類しかありません。

数値入力タイプ

また、9つの異なる数値形式しかないようです。考えられるすべての組み合わせに対して永続化列を作成しようとすることは可能です。そのためのコードの一部を以下に示します。

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;

これは出力のサンプルです:

テストコード出力

いくつかの入力値とカルチャのテーブルに追加する列を取得できませんでした。SQL Server内でカルチャのリストを見つけることができないため、考えられるすべてのカルチャを徹底的に試したわけではありません。

の決定論に関するドキュメントFORMATが正しくないと結論付けても安全だと思われるので、そのための接続項目を提出することをお勧めします。



1

私はsqlserverの通常のユーザーではないので、誤解しているかもしれませんが、私のフォーマットは文字列関数ではないようです。ドキュメントによると:

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

formatは、日付タイプまたは数値タイプのいずれかを引数として取ります。日付の年の部分を取得するだけの場合は、年関数を使用できませんか?

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

文字列表現が必要な場合:

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

1
ドキュメントでは文字列関数としてリストされています。 i.stack.imgur.com/aj0T2.png
Martin Smith

1
@MartinSmith、面白い。個人的には直感に反し、「すべての組み込み文字列関数は決定論的である」と論理的に矛盾します。
Lennart

@Lennart私はこの例の代替案に感謝しますが、例は重要ではありませんでした。
SqlZim 2017年

1
@SqlZim、私はあなたの例が単なる例であると考えましたが、念のために代替を追加しました。あなたの質問が正確に何であるかわかりません、それはフォーマットが文字列関数であるかどうか、それが決定論的であるかどうか、それともすべてが正しく文書化されているかどうかですか?
Lennart
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.