You've made a good start. I tend to query each column separately as I'm doing a conversion using templates, especially since value distributions are extremely helpful. Each data type has its quirks and custom aspects to look at. For example, for datetime data, I like to know if it is strictly dates or dates with times. And I like to know if the data fits in a smalldatetime. Knowing empty strings vs. nulls is important too.
Below is a template I use for imported datetime data interrogation
-- interrogate DateTime column
-- to see if it is a Time column, nullable, or an expected range
select
COUNT(*) as CountRows,
sum(case when <colname,,> is null then 1 else 0 end) as NullsCount,
sum(case when CONVERT(varchar(12), <colname,,>, 114) = '00:00:00:000' then 1 else 0 end) as DateNoTimeCount,
sum(case when CONVERT(varchar(12), <colname,,>, 114) > '00:00:00:000' then 1 else 0 end) as DateTimeCount,
sum(case when <colname,,> < '1/1/1900' then 1 else 0 end) as Before1900Count,
sum(case when <colname,,> between '1/1/1900' and '1/1/2000' then 1 else 0 end) as BetweenCount,
sum(case when <colname,,> >= '1/1/2000' then 1 else 0 end) as After2000Count
from
<tablename,,>
-- most frequent values
select top 100
<colname,,>,
COUNT(*) as frequencyCount
from
<tablename,,>
group by
<colname,,>
having
COUNT(*) > 1
order by
COUNT(*) desc