2016-12-05 2 views
2

Мне нужно оценить размер базы данных для предварительного условия, поэтому я пытаюсь понять, как SQL Server хранит данные в приведенном ниже примере.Оценить размер таблицы в SQL Server

В моей базе данных SQL Server, у меня есть таблица с именем InfoComp которым содержит 4 строки:

IdInfoComp : Integer Not Null (PK) 
IdDefinition : Integer Not Null (FK) 
IdObject : Integer Not Null (FK) 
Value : NVarChar(Max) Not Null 

Я хочу оценить размер таблицы. В реальном использовании, я могу получить среднюю длину, хранящуюся в Value с этим SQL запроса:

SELECT AVG(Value) FROM InfoComp 
Result : 8 

Итак, мой расчет, кажется (в байтах):

(Size(IdInfoComp) + Size(IdDefinition) + Size(IdObject) + AVG Size(Value)) * Rows count 

(4 + 4 + 4 + ((8 * 2) + 2)) * NbRows 

Но когда я пытаюсь чтобы применить этот расчет в реальном случае, это неправильно. В моем случае, у меня есть 3,250,273 строк, так что результат должен быть 92 Мб, но MS SQL Report говорит:

(Data) 147 888 KB (индексы) 113 072 KB и (зарезервирован) 261 160 КБ.

Где я ошибаюсь?

+3

Я не уверен, но быстрый поиск в Google вызывает официальную страницу MSDN с более конкретными инструкциями ... возможно, попробуйте? https://msdn.microsoft.com/en-us/library/ms175991.aspx (Похоже, возможно, он вычисляет размер индекса, который отличается от всего объема данных) – jleach

+0

Я действительно пытаюсь это сделать. Но у меня есть некоторые отличия (почти 10 МБ). – Cedric

+1

Вместо среднего, вы могли бы суммировать len 'value'. –

ответ

2

Попробуйте это ... это приблизит меня. Я использовал статью msdn для создания. Вы можете установить количество строк. Это будет делать каждая таблица в db, включая индексы. Пока не работает столбцов, и они не будут обрабатывать отношения. Он просто применит оценку rowcount к каждой таблице.

/*Do NOT change this section*/ 
GO 
CREATE TABLE RowSizes (TypeName VARCHAR(30), TableName VARCHAR(255), IndexName VARCHAR(255), Null_Bitmap SMALLINT, VariableFieldSize BIGINT, FixedFieldSize BIGINT, Row_Size BIGINT, LOBFieldSize BIGINT); 
CREATE TABLE LeafSizes (TypeName VARCHAR(30), TableName VARCHAR(255), IndexName VARCHAR(255), Row_Size BIGINT, Rows_Per_Page BIGINT, Free_Rows_Per_Page BIGINT, Non_Leaf_Levels BIGINT, Num_Leaf_Pages BIGINT, Num_Index_Pages BIGINT, Leaf_space_used_bytes BIGINT); 
GO 
CREATE PROCEDURE dbo.cp_CalcIndexPages 
    @IndexType VARCHAR(20) 
AS 
BEGIN 
    DECLARE @IndexName VARCHAR(255) 
     , @TableName varchar(255) 
     , @Non_Leaf_Levels bigint = 127 
     , @Rows_Per_Page bigint = 476 
     , @Num_Leaf_Pages bigint =10000; 

    WHILE EXISTS(SELECT TOP 1 1 FROM dbo.LeafSizes WHERE TypeName = @IndexType AND Num_Index_Pages = 0)-- AND IndexName = 'PK_ProcessingMessages') 
    BEGIN 
     SELECT TOP 1 @IndexName = IndexName 
      , @TableName = TableName 
      , @Non_Leaf_Levels = Non_Leaf_Levels 
      , @Rows_Per_Page = Rows_Per_Page 
      , @Num_Leaf_Pages = Num_Leaf_Pages 
     FROM dbo.LeafSizes 
     WHERE TypeName = @IndexType 
      AND Num_Index_Pages = 0; 

     DECLARE @Counter INT = 1 
      , @Num_Index_Pages INT = 0; 

     WHILE @Counter <= @Non_Leaf_Levels 
     BEGIN 
      BEGIN TRY 

      SELECT @Num_Index_Pages += ROUND(CASE WHEN @Num_Leaf_Pages/POWER(@Rows_Per_Page, @Counter) < CONVERT(FLOAT, 1) THEN 1 ELSE @Num_Leaf_Pages/POWER(@Rows_Per_Page, @Counter) END, 0) 
      END TRY 

      BEGIN CATCH 
       SET @Num_Index_Pages += 1 
      END CATCH 

      SET @Counter += 1 
     END 

     IF @Num_Index_Pages = 0 
      SET @Num_Index_Pages = 1; 

     UPDATE dbo.LeafSizes 
     SET Num_Index_Pages = @Num_Index_Pages 
      , Leaf_space_used_bytes = 8192 * @Num_Index_Pages 
     WHERE TableName = @TableName 
      AND IndexName = @IndexName; 

    END 
END 
GO 
/*Do NOT change above here*/ 

--Set parameters here 
DECLARE @NumRows INT = 1000000 --Number of rows for estimate 
    ,@VarPercentFill money = .6; --Percentage of variable field space used to estimate. 1 will provide estimate as if all variable columns are 100% full. 


/*Do not change*/ 
WITH cte_Tables AS (--Get Tables 
    SELECT o.object_id, s.name+'.'+o.name AS ObjectName 
    FROM sys.objects o 
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id 
    WHERE type = 'U' 
), cte_TableData AS (--Calculate Field Sizes 
    SELECT o.ObjectName AS TableName 
     , SUM(CASE WHEN t.name IN ('int', 'bigint', 'tinyint', 'char', 'datetime', 'smallint', 'date') THEN 1 ELSE 0 END) AS FixedFields 
     , SUM(CASE WHEN t.name IN ('int', 'bigint', 'tinyint', 'char', 'datetime', 'smallint', 'date') THEN c.max_length ELSE 0 END) AS FixedFieldSize 
     , SUM(CASE WHEN t.name IN ('varchar') THEN 1 ELSE 0 END) AS VariableFields 
     , SUM(CASE WHEN t.name IN ('varchar') THEN c.max_length ELSE 0 END)*@VarPercentFill AS VariableFieldSize 
     , SUM(CASE WHEN t.name IN ('xml') THEN 1 ELSE 0 END) AS LOBFields 
     , SUM(CASE WHEN t.name IN ('xml') THEN 10000 ELSE 0 END) AS LOBFieldSize 
     , COUNT(1) AS TotalColumns 
    FROM sys.columns c 
    INNER JOIN cte_Tables o ON o.object_id = c.object_id 
    INNER JOIN sys.types t ON c.system_type_id = t.system_type_id 
    GROUP BY o.ObjectName 
), cte_Indexes AS (--Get Indexes and size 
    SELECT s.name+'.'+o.name AS TableName 
     , ISNULL(i.name, '') AS IndexName 
     , i.type_desc 
     , i.index_id 
     , SUM(CASE WHEN t.name IN ('tinyint','smallint', 'int', 'bigint', 'char', 'datetime', 'date') AND c.key_ordinal > 0 THEN 1 ELSE 0 END) AS FixedFields 
     , SUM(CASE WHEN t.name IN ('tinyint','smallint', 'int', 'bigint', 'char', 'datetime', 'date') AND c.key_ordinal > 0 THEN tc.max_length ELSE 0 END) AS FixedFieldSize 
     , SUM(CASE WHEN t.name IN ('varchar') AND c.key_ordinal > 0 THEN 1 ELSE 0 END) AS VariableFields 
     , SUM(CASE WHEN t.name IN ('varchar') AND c.key_ordinal > 0 THEN tc.max_length ELSE 0 END)*@VarPercentFill AS VariableFieldSize 
     , SUM(CASE WHEN t.name IN ('xml') AND c.key_ordinal > 0 THEN 1 ELSE 0 END) AS LOBFields 
     , SUM(CASE WHEN t.name IN ('xml') AND c.key_ordinal > 0 THEN 10000 ELSE 0 END) AS LOBFieldSize 
     , SUM(CASE WHEN t.name IN ('tinyint','smallint', 'int', 'bigint', 'char', 'datetime', 'date') AND c.is_included_column > 0 THEN 1 ELSE 0 END) AS FixedIncludes 
     , SUM(CASE WHEN t.name IN ('tinyint','smallint', 'int', 'bigint', 'char', 'datetime', 'date') AND c.is_included_column > 0 THEN 1 ELSE 0 END) AS FixedIncludesSize 
     , SUM(CASE WHEN t.name IN ('varchar') AND c.is_included_column > 0 THEN 1 ELSE 0 END)*@VarPercentFill AS VariableIncludes 
     , SUM(CASE WHEN t.name IN ('varchar') AND c.is_included_column > 0 THEN tc.max_length ELSE 0 END) AS VariableIncludesSize 
     , COUNT(1) AS TotalColumns 
    FROM sys.indexes i 
    INNER JOIN sys.columns tc ON i.object_id = tc.object_id 
    INNER JOIN sys.index_columns c ON i.index_id = c.index_id 
     AND c.column_id = tc.column_id 
     AND c.object_id = i.object_id 
    INNER JOIN sys.objects o ON o.object_id = i.object_id AND o.is_ms_shipped = 0 
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id 
    INNER JOIN sys.types t ON tc.system_type_id = t.system_type_id 
    GROUP BY s.name+'.'+o.name, ISNULL(i.name, ''), i.type_desc, i.index_id 
) 
INSERT RowSizes 
SELECT 'Table' AS TypeName 
    , n.TableName 
    , '' AS IndexName 
    , 2 + ((n.FixedFields+n.VariableFields+7)/8) AS Null_Bitmap 
    , 2 + (n.VariableFields * 2) + n.VariableFieldSize AS Variable_Data_Size 
    , n.FixedFieldSize 
    /*FixedFieldSize + Variable_Data_Size + Null_Bitmap*/ 
    , n.FixedFieldSize + (2 + (n.VariableFields * 2) + (n.VariableFieldSize)) + (2 + ((n.FixedFields+n.VariableFields+7)/8)) + 4 AS Row_Size 
    , n.LOBFieldSize 
FROM cte_TableData n 
UNION 
SELECT i.type_desc 
    , i.TableName 
    , i.IndexName 
    , 0 AS Null_Bitmap 
    , CASE WHEN i.VariableFields > 0 THEN 2 + (i.VariableFields * 2) + i.VariableFieldSize + 4 ELSE 0 END AS Variable_Data_Size 
    , i.FixedFieldSize 
    /*FixedFieldSize + Variable_Data_Size + Null_Bitmap if not clustered*/ 
    , i.FixedFieldSize + CASE WHEN i.VariableFields > 0 THEN 2 + (i.VariableFields * 2) + i.VariableFieldSize + 4 ELSE 0 END + 7 AS Row_Size 
    , i.LOBFieldSize 
FROM cte_Indexes i 
WHERE i.index_id IN(0,1) 
UNION 
SELECT i.type_desc 
    , i.TableName 
    , i.IndexName 
    , CASE WHEN si.TotalColumns IS NULL THEN 2 + ((i.FixedFields+i.VariableFields+i.VariableIncludes+i.FixedIncludes+8)/8) 
      ELSE 2 + ((i.FixedFields+i.VariableFields+i.VariableIncludes+i.FixedIncludes+7)/8) 
     END AS Null_Bitmap 
    , CASE WHEN si.TotalColumns IS NULL THEN 2 + ((i.VariableFields + 1) * 2) + (i.VariableFieldSize + 8) 
      ELSE 2 + (i.VariableFields * 2) + i.VariableFieldSize 
     END AS Variable_Data_Size 
    , CASE WHEN si.TotalColumns IS NULL THEN si.FixedFieldSize 
      ELSE i.FixedFieldSize + si.FixedFieldSize 
     END AS FixedFieldSize 
    /*FixedFieldSize + Variable_Data_Size + Null_Bitmap if not clustered*/ 
    , CASE WHEN si.TotalColumns IS NULL THEN i.FixedFieldSize + (2 + ((i.VariableFields + 1) * 2) + (i.VariableFieldSize + 8)) + (2 + ((i.TotalColumns+8)/8)) + 7 
      ELSE i.FixedFieldSize + (2 + (i.VariableFields * 2) + i.VariableFieldSize) + (2 + ((i.TotalColumns+7)/8)) + 4 
     END AS Row_Size 
    , i.LOBFieldSize 
FROM cte_Indexes i 
LEFT OUTER JOIN cte_Indexes si ON i.TableName = si.TableName AND si.type_desc = 'CLUSTERED' 
WHERE i.index_id NOT IN(0,1) AND i.type_desc = 'NONCLUSTERED'; 

--SELECT * FROM RowSizes 

/*Calculate leaf sizes for tables and HEAPs*/ 
INSERT LeafSizes 
SELECT r.TypeName 
    , r.TableName 
    ,'' AS IndexName 
    , r.Row_Size 
    , 8096/(r.Row_Size + 2) AS Rows_Per_Page 
    , 8096 * ((100 - 90)/100)/(r.Row_Size + 2) AS Free_Rows_Per_Page 
    , 0 AS Non_Leaf_Levels 
    /*Num_Leaf_Pages = Number of Rows/(Rows_Per_Page - Free_Rows_Per_Page) OR 1 if less than 1*/ 
    , CASE WHEN @NumRows/((8096/(r.Row_Size + 2)) - (8096 * ((100 - 90)/100)/(r.Row_Size + 2))) < 1 
      THEN 1 
      ELSE @NumRows/((8096/(r.Row_Size + 2)) - (8096 * ((100 - 90)/100)/(r.Row_Size + 2))) 
     END AS Num_Leaf_Pages 
    , 0 AS Num_Index_Pages 
    /*Leaf_space_used = 8192 * Num_Leaf_Pages*/ 
    , 8192 * CASE WHEN @NumRows/((8096/(r.Row_Size + 2)) - (8096 * ((100 - 90)/100)/(r.Row_Size + 2))) < 1 
       THEN 1 
       ELSE @NumRows/((8096/(r.Row_Size + 2)) - (8096 * ((100 - 90)/100)/(r.Row_Size + 2))) 
      END + (@NumRows * LOBFieldSize) AS Leaf_space_used_bytes 
FROM RowSizes r 
WHERE r.TypeName = 'Table' 
ORDER BY TypeName, TableName; 

/*Calculate leaf sizes for CLUSTERED indexes*/ 
INSERT LeafSizes 
SELECT r.TypeName 
    , r.TableName 
    , r.IndexName 
    , r.Row_Size 
    , 8096/(r.Row_Size + 2) AS Rows_Per_Page 
    , 0 AS Free_Rows_Per_Page 
    , 1 + ROUND(LOG(8096/(r.Row_Size + 2)), 0)*(l.Num_Leaf_Pages/(8096/(r.Row_Size + 2))) AS Non_Leaf_Levels 
    , l.Num_Leaf_Pages 
    , 0 AS Num_Index_Pages 
    , 0 AS Leaf_space_used_bytes 
FROM RowSizes r 
INNER JOIN LeafSizes l ON r.TableName = l.TableName AND l.TypeName = 'Table' 
WHERE r.TypeName = 'CLUSTERED'; 

PRINT 'CLUSTERED' 
EXEC dbo.cp_CalcIndexPages @IndexType = 'CLUSTERED' 

/*Calculate leaf sizes for NONCLUSTERED indexes*/ 
INSERT LeafSizes 
SELECT r.TypeName 
    , r.TableName 
    , r.IndexName 
    , r.Row_Size 
    , 8096/(r.Row_Size + 2) AS Rows_Per_Page 
    , 0 AS Free_Rows_Per_Page 
    , 1 + ROUND(LOG(8096/(r.Row_Size + 2)), 0)*(l.Num_Leaf_Pages/(8096/(r.Row_Size + 2))) AS Non_Leaf_Levels 
    , l.Num_Leaf_Pages 
    , 0 AS Num_Index_Pages 
    , 0 AS Leaf_space_used_bytes 
FROM RowSizes r 
INNER JOIN LeafSizes l ON r.TableName = l.TableName AND l.TypeName = 'Table' 
WHERE r.TypeName = 'NONCLUSTERED'; 

PRINT 'NONCLUSTERED' 
EXEC dbo.cp_CalcIndexPages @IndexType = 'NONCLUSTERED' 

SELECT * 
FROM dbo.LeafSizes 
--WHERE TableName = 'eligibility.clientrequest' 

SELECT TableName 
    , @NumRows AS RowsPerTable 
    , @VarPercentFill*100 AS VariableFieldFillFactor 
    , SUM(CASE WHEN TypeName = 'Table' THEN Leaf_space_used_bytes ELSE 0 END)/1024/1024 AS TableSizeMB 
    , SUM(Leaf_space_used_bytes)/1024/1024 AS SizeWithIndexesMB 
FROM LeafSizes 
--WHERE TableName = 'eligibility.clientrequest' 
GROUP BY TableName 
ORDER BY TableName; 


GO 
/*Cleanup when done*/ 
DROP PROCEDURE dbo.cp_CalcIndexPages; 
DROP TABLE dbo.RowSizes; 
DROP TABLE dbo.LeafSizes; 
0

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

Первое, что вы должны знать, что размер любой базы данных SQL Server больше или равен размеру базы данных model. Это потому, что база данных model является шаблоном для новых баз данных и поэтому она копируется каждый раз при выполнении инструкции CREATE DATABASE.

Вся информация в базе данных хранится на страницах 8 КБ на диске. Существует много типов страниц. Некоторые из них (например, карты распределения и метаданные) используются для внутренних целей, а другие - для хранения данных.

Размер таблицы зависит от того, как данные организованы на диске (имеет ли он кластеризованный индекс или нет), типы столбцов и сжатие данных. Размер индекса зависит от наличия уникального индекса в индексированной таблице, подсчета уровня индекса, коэффициента заполнения и т. Д.

Как я уже говорил, все хранится на страницах и в данных. SQL Server имеет страницы для данных в строке, страницы для данных переполнения строк и страницы для данных LOB. Страницы данных состоят из трех основных частей: заголовка страницы, строк данных и массива смещения данных.

Заголовок страницы занимает первые 96 байт каждой страницы данных, оставляя 8 096 байт для других компонентов. Массив смещения строк представляет собой блок из 2-байтных записей, который хранится в конце страницы. Счетчик записей хранится в заголовке и называется количеством слотов.

Площадь между заголовком и массивом смещения строк - это область, в которой хранятся строки данных. Каждая строка состоит из двух компонентов: части фиксированного размера и части переменной длины.

Структура строк данных является:

  • Статус Биты А, 1 байт
  • Статус Биты В, 1 байт
  • фиксированной длины размер (FSIZE), 2 байта
  • Фиксированная данные длины (FData), FSIZE - 4
  • Количество столбцов (Ncol), 2 байта
  • NULL, растровый, потолочный (Ncol/8)
  • Количество переменной длины столбцов, хранящихся в строке (VarCount), 2 байта
  • Переменная колонка смещения массива (VarOffset), 2 * VarCount
  • переменной длины данных (VarData), VarOff [VarCount] - (FSIZE + 4 + Потолок (Ncol/8) + 2 * VarCount)

Примечание индекс строки хранятся в том же образом, как и строки данных.

Не все, что я здесь объяснил, но я надеюсь, что это поможет вам понять, для каких целей SQL Server использует выделенное пространство. Также следует иметь в виду, что файлы базы данных растут по размеру, указанному опцией FILEGROWTH, и это может привести к большему реальному размеру, чем оценка.

Также ознакомьтесь с книгой Microsoft SQL Server 2012 Internals и прочитайте как Estimate the Size of a Database. Это, вероятно, будет интересно для вас.

Смежные вопросы