The 'two approaches' answer suggested by Anon is helpful. The website's comment box doesn't allow enough text so I will post my final answer here.
The linked answer has special provision for user data types, which my database doesn't have, so I am using the type_name
builtin instead. This query tries to reverse-engineer the type for each column:
select t.name,
c.name,
case
when type_name(c.system_type_id) in (
'int', 'real', 'float', 'date', 'time', 'datetime', 'datetime2',
'tinyint', 'smallint', 'smalldatetime', 'bit', 'bigint', 'timestamp',
'image'
) then type_name(c.system_type_id)
else type_name(c.system_type_id) + '('
+ case
when precision = 0 then convert(varchar(10), c.max_length)
else convert(varchar(10), precision) + ', ' + convert(varchar(10), scale)
end
+ ')'
end as ty
from sys.tables t
join sys.columns c
on t.object_id = c.object_id
where c.is_nullable = 1
and c.is_computed = 0
and t.schema_id = 1
order by t.name,
c.name
Then you can take each row from this query and do a check that no nulls exist before running 'alter table'. I am doing something like the following:
select case when
exists (select 0 from TABLE)
and not exists (select 0 from TABLE tablesample (1000 rows) where COLUMN is null)
then 1 else 0 end
for each TABLE, COLUMN returned by the first query. If the second query returns 1 then you can probably make the 'alter table' change. I use tablesample above to stop this being too heavy on the database, since I plan to run the check regularly; if the size of the table as returned by sp_spaceused is less than 100 kilobytes then I omit the tablesample clause.
Or, if you feel brave you could just run all the 'alter table' statements and let them fail if the column does contain nulls.
Oddly, I don't have permissions on the database to right-click in Management Studio and 'script database as', although I can do it for individual objects.