sqlsql-servermaxinformation-schemadatalength

Retrieving datalength along with columns and tables in SQL Server query


Because I want to convert the columns to not be varchar(MAX) I want to see the maximum datalength for each column to decide what the new size should be.

I have this query for finding all my (n)varchar(MAX) columns.

SELECT [TABLE_NAME], [COLUMN_NAME]
FROM information_schema.columns
WHERE DATA_TYPE IN ('varchar', 'nvarchar')
  AND CHARACTER_MAXIMUM_LENGTH = -1
ORDER BY TABLE_NAME, COLUMN_NAME

For instance I have a customer table and among the results the following is output for my customers table

+------------+--------------+
| TABLE_NAME | COLUMN_NAME  |
+------------+--------------+
| customers  | name         |
| customers  | address      |
| customers  | postal_code  |
| customers  | city         |
| customers  | email        |
| customers  | phone_number |
+------------+--------------+

By running the following queries:

SELECT MAX(DATALENGTH(name)) FROM customers
SELECT MAX(DATALENGTH(address)) FROM customers
SELECT MAX(DATALENGTH(postal_code)) FROM customers
SELECT MAX(DATALENGTH(city)) FROM customers
SELECT MAX(DATALENGTH(email)) FROM customers
SELECT MAX(DATALENGTH(phone_number)) FROM customers

I can get the result I want, but I'd really like for it to be just one query returning something like:

+------------+--------------+------------+
| TABLE_NAME | COLUMN_NAME  | Datalength |
+------------+--------------+------------+
| customers  | name         |         93 |
| customers  | address      |        122 |
| customers  | postal_code  |          6 |
| customers  | city         |         44 |
| customers  | email        |         75 |
| customers  | phone_number |         18 |
+------------+--------------+------------+

I have tried

SELECT 
    [TABLE_NAME], [COLUMN_NAME], 
    (SELECT MAX(DATALENGTH(COLUMN_NAME))
     FROM TABLE_NAME) AS 'MaxContentLength'
FROM information_schema.columns
WHERE DATA_TYPE IN ('varchar', 'nvarchar')
  AND CHARACTER_MAXIMUM_LENGTH = -1
ORDER BY TABLE_NAME, COLUMN_NAME

But I get this error:

Msg 208, Level 16, State 1, line 1
Invalid object name 'TABLE_NAME'

How do I fix this issue (or is there another way to do what I want?)


Solution

  • This is my approach to solve your question, maybe not the fastest one.

    declare @tbl varchar(128), @fld varchar(128)
    declare @res table (
        [Table_Name] varchar(128), [Column_Name] varchar(128), [DataLength] int)
    
    declare c1 cursor local for
        select c.TABLE_NAME, c.COLUMN_NAME
        from INFORMATION_SCHEMA.COLUMNS c
        join INFORMATION_SCHEMA.TABLES t on 
            (t.TABLE_CATALOG = c.TABLE_CATALOG and 
             t.TABLE_SCHEMA = c.TABLE_SCHEMA and 
             t.TABLE_NAME = c.TABLE_NAME)
        where t.TABLE_TYPE <> 'VIEW' 
            and c.DATA_TYPE in ('varchar', 'nvarchar') 
            and c.CHARACTER_MAXIMUM_LENGTH = -1
    open c1
    fetch next from c1 into @tbl, @fld
    while @@FETCH_STATUS=0
    begin
        insert into @res
        exec ('select '''
              +@tbl+''' as [TABLE_NAME], '''
              +@fld+''' as [COLUMN_NAME], max(datalength('
              +@fld+')) as [DataLength] from '
              +@tbl)
        fetch next from c1 into @tbl, @fld
    end    
    close c1
    deallocate c1
    
    select * from @res