Una función no es necesariamente determinista o no determinista. Hay algunas funciones que pueden ser deterministas según cómo se usen :
Las siguientes funciones no siempre son deterministas, pero se pueden usar en vistas indexadas o índices en columnas calculadas cuando se especifican de manera determinista.
CAST
y CONVERT
son tales ejemplos. Según las pruebas que ha realizado hasta ahora, creo que es justo decir que FORMAT
no siempre es determinista, a pesar de ser una función de cadena. Si desea saber si a veces es determinista, la única técnica que se me ocurre es probar suficientes formas diferentes de llamarlo hasta que esté satisfecho. Por ejemplo, consideremos FORMAT
que se aplica a los números. Solo hay diez tipos de entrada numérica diferentes :
También parece haber solo nueve formatos numéricos diferentes . Es posible intentar crear columnas persistentes para todas las combinaciones posibles. Algún código para hacerlo está a continuación:
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;
Aquí hay una muestra de la salida:
No pude agregar ninguna de las columnas a la tabla para algunos valores de entrada y culturas. No probé exhaustivamente todas las culturas posibles porque no puedo encontrar una lista de ellas en SQL Server.
Como mínimo, parece seguro concluir que la documentación sobre el determinismo de FORMAT
es incorrecta, por lo que recomendaría enviar un elemento de conexión para ello.
alter table #t add date_formatted_01 as CONVERT(VARCHAR(20), FORMAT(date_col, 'YYYY', 'en-US')) persisted;
. No estoy seguro de por quéFORMAT
no es determinista, especialmente al especificar la cultura. Ladate_formatted
columna puede serVARCHAR(20)
(aún persistente) y configurarse mediante Trigger usandoFORMAT
. O SQLCLR funciona. Usando la biblioteca SQL # SQLCLR (que escribí), puede hacerloALTER TABLE SQL#.t ADD date_formatted_03 AS SQL#.Date_Format(date_col, 'd', 'en-US') PERSISTED;
(la tabla es propiedad de SQL # ya que el propietario de la tabla y la función deben ser los mismos).