Thanks to bilinkc!
That was the solution for me.
I made it work on MSSQL 2019 and added the is_nullable flag.
So with this script you can alter all columns in the named table and change the datatype including nullable and not nullable fields correctly.
declare @TableName varchar(255) = 'TableName';
declare @DataTypeOld varchar(max) = 'float';
declare @DataTypeNew varchar(max) = 'decimal (19,4)';
DECLARE csrTemp CURSOR FOR
SELECT
SC.name AS column_name, SC.is_nullable
FROM
sys.tables ST
INNER JOIN
sys.columns SC
ON SC.object_id = ST.object_id
INNER JOIN
sys.types T
ON T.system_type_id = SC.system_type_id
WHERE
ST.name = @TableName
AND T.name = @DataTypeOld
OPEN csrTemp;
DECLARE
-- this holds the current column name
@column_name sysname,
@is_nullable bit,
@base_query varchar(max),
@actual_query varchar(max);
-- template query for changing the datatype
SET @base_query = 'ALTER TABLE '+@TableName+' ALTER COLUMN [<X/>] '+ @DataTypeNew;
declare @Count int = 0;
FETCH NEXT FROM csrTemp
INTO @column_name, @is_nullable;
WHILE (@@FETCH_STATUS <> -1) BEGIN
IF (@@FETCH_STATUS <> -2)
BEGIN
SET @Count = @Count +1;
BEGIN TRY
SET @actual_query = REPLACE(@base_query, '<X/>', @column_name);
IF @is_nullable = 1
SET @actual_query = @actual_query + ' NULL';
ELSE
SET @actual_query = @actual_query + ' NOT NULL';
EXECUTE (@actual_query);
PRINT @actual_query;
END TRY
BEGIN CATCH
SET @Count = @Count -1;
PRINT '---------------------------';
PRINT 'Failed executing statement: '+@actual_query;
PRINT 'ERROR: '+ ERROR_MESSAGE();
PRINT '';
END CATCH
END
FETCH NEXT FROM csrTemp
INTO @column_name, @is_nullable;
END
CLOSE csrTemp;
DEALLOCATE csrTemp;
PRINT '---------------------------';
print 'Altered '+ cast(@Count as varchar) + ' columns.';