Как вывести отчет по использованию памяти каждым столбцом в таблице MS SQL?

Рейтинг: 0Ответов: 2Опубликовано: 17.03.2023

Мне нужно узнать, сколько весит каждый столбец в таблице. Могу для этого использовать

sum(datalength(столбец))

Но для каждого столбца это очень долго выводить, потому что таких столбцов десятки. Как просмотреть информацию о занимаемой памяти по каждому столбцу таблицы?

То есть в отчете я хочу видеть 2 столбца: название колонки и занимаемая память (отсортированные по памяти по убыванию)

Ответы

▲ 1Принят

Пожалуйста, попробуйте следующее решение.

Оно будет работать, начиная с SQL Server 2017 и более поздних версий.

Дополнительные пояснения

  • SQL Server размещает таблицы на основе 8-килобайтных страниц. не на фактических размерах записей (rows).
  • UPDATE/DELETE не работает in-place, они создают дополнительные фрагментации.

SQL

-- DDL и образец вставки данных, начало
USE tempdb;
GO

DROP TABLE IF EXISTS dbo.tbl;

CREATE TABLE tbl  (ID INT IDENTITY PRIMARY KEY, city VARCHAR(20), State CHAR(2), Population INT, InsertedOn DATETIMEOFFSET(3) DEFAULT SYSDATETIMEOFFSET());
INSERT tbl (city, State, Population, InsertedOn) VALUES
('Miami', 'FL', 350000, Default),
('Orlando', 'FL', 300000, Default),
('Dallas', 'TX', 5000000, Default);
-- DDL и образец вставки данных, конец

SELECT * FROM tbl;

DECLARE @tbl_catalog sysname = 'tempdb'
    , @tbl_name sysname = 'tbl'
    , @schema_name sysname = 'dbo'
    , @columnList VARCHAR(MAX)
    , @CrLf NCHAR(2) = NCHAR(13) + NCHAR(10)
    , @Tab NCHAR(1) = NCHAR(9)
    , @SQL NVARCHAR(MAX);

-- SQL Server 2017 onwards
SET @columnList = (SELECT STRING_AGG(CONCAT(QUOTENAME(COLUMN_NAME),'='
        ,'SUM(DATALENGTH(',QUOTENAME(COLUMN_NAME),'))',@CrLf,@Tab), ',')
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_CATALOG = @tbl_catalog
        AND TABLE_SCHEMA = @schema_name
        AND TABLE_NAME = @tbl_name
);

PRINT @columnList;

SET @SQL =
N';WITH rs(xmldata) AS
(
    SELECT ' + @columnList +
N'FROM tbl
    FOR XML PATH(''''), TYPE, ROOT(''root'')
)
SELECT column_name = c.value(''local-name(.)'', ''SYSNAME'') 
    , column_value_bytes = c.value(''text()[1]'', ''INT'') 
FROM rs
CROSS APPLY xmldata.nodes(''/root/*'') AS t(c)
ORDER BY column_value_bytes DESC;';

PRINT @SQL;

EXEC sp_executesql @SQL;

Результат

column_name column_value_bytes
InsertedOn 27
city 18
ID 12
Population 12
State 6
▲ 0

Если у таблицы есть индексы, то они тоже занимают какое-то место. Изредка суммарный объём индексов может превышать объём данных таблицы.

Если у Вас таблица со сжатием (на уровне СТРОК или на уровне СТРАНИЦ), то при некоторых ситуациях логический объём данных может превышать физический.

При вставке новой записи в таблицу, может произойти "расщепление" существующей 8k-страницы на две,- таким образом занимаемый объём таблицы вырастет не на "длину" записи а сразу на 8k. Поэтому, даже просуммировав длинну (в байтах) содержимого всех столбцов, можно получить только приблизительные объём. На диске данные базы данных хранятся (и обрабатываются в памяти MS SQL Server) экстентами - страницами по 8kbyte.