Skript zum Schätzen der Zeilengröße für jede Tabelle

8

Ich versuche, den Speicherplatzbedarf für einen zentralen Datenbankserver abzuschätzen, der Daten aus etwa 200 identischen Felddatenbanken sammelt. Ich habe die durchschnittliche tägliche Zeilenanzahl für jede Tabelle und muss jetzt die Zeilengröße einschließlich der Indizes für jede Tabelle schätzen.

Gibt es so ein Tier oder muss ich mein eigenes rollen? Können Sie mir einen guten Ansatz vorschlagen, wenn ich meinen eigenen rollen muss?

TIA

Metapher
quelle
Einige Infos hier: sqlskills.com/blogs/paul/…
Aaron Bertrand
Wenn Sie die Größe einer Tabelle in einer der größten Felddatenbanken durch die Anzahl der Zeilen dividieren, sollten Sie eine angemessene Schätzung der wahrscheinlichen Größe pro Zeile der endgültigen Datenbank erhalten. Die Speicherung ist mit der Zeilenanzahl nahezu linear. Vielleicht fehlt mir etwas - brauchen Sie eine genauere Nummer?
Jon of All Trades
Die tägliche durchschnittliche Anzahl neuer Zeilen beträgt 85.000.000. Ich möchte so nah wie möglich an die Realität heranrücken.
Metapher

Antworten:

9

Unter Verwendung von Aarons Link für eine noch feinere Anpassung meines Prozesses würde ich eine Funktion und eine Ansicht / Abfrage empfehlen, um die Größe pro Zeile und Tabelle einschließlich der Indizes anzugeben.

create function dbo.getColumnSize (@typeName SYSNAME, @max_length INT, @precision INT)
RETURNS INT
AS
BEGIN
    RETURN (SELECT CASE @typeName
        WHEN 'tinyint'          THEN 1
        WHEN 'smallint'         THEN 2
        WHEN 'int'              THEN 4
        WHEN 'bigint'           THEN 8
        WHEN 'numeric'          THEN ((@precision - 1)/2) + 1
        WHEN 'decimal'          THEN ((@precision - 1)/2) + 1
        WHEN 'real'             THEN 4
        WHEN 'float'            THEN CASE WHEN @precision <=24 THEN 4 ELSE 8 END
        WHEN 'money'            THEN 8
        WHEN 'smallmoney'       THEN 4
        WHEN 'time'             THEN 5
        WHEN 'timestamp'        THEN 5
        WHEN 'date'             THEN 3
        WHEN 'smalldatetime'    THEN 4
        WHEN 'datetime'         THEN 8
        WHEN 'datetime2'        THEN 8
        WHEN 'datetimeoffset'   THEN 10
        WHEN 'char'             THEN @max_length
        WHEN 'varchar'          THEN @max_length + 2
        WHEN 'nchar'            THEN @max_length
        WHEN 'nvarchar'         THEN @max_length + 2
        WHEN 'binary'           THEN @max_length
        WHEN 'varbinary'        THEN @max_length + 2
        WHEN 'bit'              THEN 0.125
    END)
END

select SchemaName, ObjectName, SUM(CEILING(Bytes))+4+2+2+2+(ceiling(count(distinct columnname)*1.0/8.0)) As RowSize--, FLOOR((POWER(2,30)+(POWER(2,30)-1))*1.0/SUM(CEILING(Bytes))/30/2/60/24)
from (
    select s.name as SchemaName, o.name AS ObjectName, c.name as ColumnName, t.name as TypeName
        , dbo.getColumnSize(t.name,c.max_length, c.precision) AS Bytes
    from sys.objects o
        inner join sys.schemas s on s.schema_id=o.schema_id
        inner join sys.columns c on o.object_id=c.object_id
        inner join sys.types t on c.system_type_id=t.system_type_id
            and t.user_type_id=c.user_type_id
    where o.type='U'
        AND RIGHT(t.name,4) != 'text'
    UNION ALL
    select s.name as SchemaName, o.name AS ObjectName, c.name as ColumnName, t.name as TypeName
        , dbo.getColumnSize(t.name,c.max_length, c.precision) AS Bytes
    from sys.objects o
        inner join sys.schemas s on s.schema_id=o.schema_id
        inner join sys.indexes i on i.object_id = o.object_id
        inner join sys.index_columns ic on ic.object_id = o.object_id
            and i.index_id = ic.index_id
        inner join sys.columns c on o.object_id=c.object_id
            and ic.column_id = c.column_id
        inner join sys.types t on c.system_type_id=t.system_type_id
            and t.user_type_id=c.user_type_id
    where o.type='U'
        AND RIGHT(t.name,4) != 'text'
    ) Z
group by SchemaName, ObjectName
Jaaz Cole
quelle
Sollte die Größe von nchar und nvarchar nicht doppelt so groß sein wie ihre Länge?
Stefan Sieber
1
StefanSieber, Sie würden es denken, und ich dachte es auch, aber beim Testen stellte ich fest, dass die sys.columns-Ansicht von SQL Server max_length in Bytes und nicht in Zeichen meldet. so wird nvarchar (1) max_length von 2 melden.
Jaaz Cole
wie die Eleganz der Antwort und ich werde sie wahrscheinlich verwenden ... mit einer geringfügigen Änderung, wenn 'bit' eine Nicht-Ganzzahl zurückgibt. Je nach Anwendung der resultierenden Daten entweder abrunden oder aufrunden.
yeOldeDataSmythe
9

Als Antwort ausgewähltes Skript hat einige Nachteile:

  1. Berücksichtigt einige SQL Server-Datentypen nicht:

    Bild, Text, eindeutige Kennung, sql_variant, ntext, hierarchyid, Geometrie, Geografie, XML, Systemname

und (max) Datenstrukturstrukturen wie varchar (max), um nur einige zu nennen.

  1. Berücksichtigt berechnete Spalten, die die Größe des Datensatzes nicht beeinflussen sollten.
  2. Berücksichtigt nicht, dass die Faktentabelle NULL-fähige Spalten enthält.
  3. Berücksichtigt nicht die Tatsache, dass einige Datentypen je nach angegebenem Maßstab unterschiedliche Größen haben (z. B. sollten Zeit (0) und Zeit (7) unterschiedliche Spaltenlängen haben).

Das folgende Skript zeigt die Größe des Datensatzes auf einer Datenseite. Es zeigt die maximale Größe, die vom Datensatz angenommen werden kann, die minimale Größe (falls einer alle Werte im Datensatz NULL sind, wenn dies durch die Tabellenstruktur zulässig ist). Es zeigt den Tabellentyp (CLUSTERED / NONCLUSTERED), die Gesamtzahl der Datenspalten und das Schema, zu dem die Tabelle gehört.

Beachten Sie, dass dieses Skript keine Indizes berücksichtigt.

Update: Das Skript zählt jede Bitspalte als 1/8 eines Bytes. Die minimalen und maximalen Größen werden angepasst, um sicherzustellen, dass die Datensätze nicht kleiner als die Größe des Weiterleitungsstubs sein dürfen .

  SELECT 
     -- record cannot be smaller than the forwarding stub size =9 Bytes
     CASE WHEN [Max Size]>=9
     THEN [Max Size]
     ELSE 9 
     END AS [Max Size]
     -- record cannot be smaller than the forwarding stub size =9 Bytes
    , CASE WHEN [Min Size]>=9
     THEN [Min Size]
     ELSE 9 
     END AS [Min Size]
    , [Table Name]
    , [Table Type]
    , [Total Number of Columns]
    , [Schema]
    FROM
    (
SELECT 
    DISTINCT 
    -- Overhead for row header of a data row
    4
    +
    -- Overhead for NULL bitmap
    2+cast(([Total Number of Columns]+7)/8 AS BIGINT)+
    -- overhead for variable length
    CASE WHEN [IsVariableLength]>0
    THEN
    2
    ELSE
    0
    END
    +  
    --- Sum is on record level
    SUM(
    a1.[max_length]
    +
    -- Overhead for variable-length columns
    CASE WHEN 
    -- varchar
    [System Type]='varchar'
    --(([system_type_id]=167) AND ([user_type_id]=167))
    OR
    -- nvarchar 
    [System Type]='nvarchar'
    --(([system_type_id]=231) AND ([user_type_id]=231))
    OR
    -- IMAGE
    (([system_type_id]=34) OR ([user_type_id]=34))
    OR
    -- TEXT
    (([system_type_id]=35) OR ([user_type_id]=35))
    OR 
    --  NTEXT
    (([system_type_id]=99) OR ([user_type_id]=99))
    OR 
    --  SQLVARIANT
    (([system_type_id]=98) OR ([user_type_id]=98))
    OR
    -- hierarchyid geometry geography
    (([system_type_id]=240))
    THEN 2
    ELSE 0
    END
    )
    OVER (PARTITION BY a1.[Schema], a1.[Table Name]) AS [Max Size]

    , -- Overhead for row header of a data row
    4
    +
    -- Overhead for NULL bitmap
    2+cast(([Total Number of Columns]+7)/8 AS BIGINT)+
    -- overhead for variable length
    CASE WHEN ([IsVariableLength]>0) AND ([AnyFixedColumn]=0)
    THEN
    2
    ELSE
    0
    END
    +
    --- Sum is on record level
    SUM(
    -- overhead for variable length depending on number of variable columns
    CASE WHEN 
    -- varchar
    --[System Type]='varchar'
    (([system_type_id]=167) OR ([user_type_id]=167))
    OR
    -- nvarchar 
    --[System Type]='nvarchar'
    (([system_type_id]=231) OR ([user_type_id]=231))
    OR
    -- IMAGE
    (([system_type_id]=34) OR ([user_type_id]=34))
    OR
    -- TEXT
    (([system_type_id]=35) OR ([user_type_id]=35))
    OR 
    --  NTEXT
    (([system_type_id]=99) OR ([user_type_id]=99))
    --  VARBINARY
    OR
    (([system_type_id]=165) OR ([user_type_id]=165))
    OR 
    --  SQLVARIANT
    (([system_type_id]=98) OR ([user_type_id]=98))
    OR
    -- hierarchyid geometry geography
    (([system_type_id]=240))
    OR
    -- xml
    (([system_type_id]=241))
    THEN
        CASE WHEN [Is Nullable]=1
        THEN 0 
        ELSE 
        1
        END
    ELSE
        CASE
        WHEN
        -- bit
        (([system_type_id]=104) OR ([user_type_id]=104))
        and [Is Nullable]=1
        THEN 0
        ELSE
        a1.[max_length]
        END
    END
    -- 

    )
    OVER (PARTITION BY a1.[Schema], a1.[Table Name]) AS [Min Size]
    , a1.[Table Name]
    , [Table Type]
    , [Total Number of Columns]
    , a1.[Schema]
    FROM
    -- Start a1
    (   SELECT
        (SELECT [name] FROM [sys].[schemas]
        WHERE [sys].[schemas].[schema_id]=[sys].[objects].[schema_id])
        AS [Schema]
        , [sys].[objects].[name] AS [Table Name]
        , [sys].[all_columns].[name] AS [Column Name]
        , [sys].[all_columns].[system_type_id]
        , (
            SELECT name FROM [sys].[types]
            WHERE [sys].[types].[system_type_id]=[sys].[all_columns].[system_type_id]
            AND
                    [sys].[types].[user_type_id]=[sys].[all_columns].[user_type_id]
            ) AS [System Type]
        , [sys].[all_columns].[user_type_id]
        , 
        CASE 
        WHEN 
        -- IMAGE
        (([system_type_id]=34) OR ([user_type_id]=34))
        THEN 2147483647
        -- TEXT
        WHEN (([system_type_id]=35) OR ([user_type_id]=35))
        THEN 2147483647
        --  NTEXT
        WHEN (([system_type_id]=99) OR ([user_type_id]=99))
        THEN 1073741823
        -- varchar(max)
        WHEN (([system_type_id]=167) OR ([user_type_id]=167)) AND ([sys].[all_columns].[max_length]=-1)
        THEN 2147483647
        -- nvarchar(max) 
        WHEN (([system_type_id]=231) OR ([user_type_id]=231)) AND ([sys].[all_columns].[max_length]=-1)
        THEN 1073741823
        -- varbinary(max)
        WHEN (([system_type_id]=165) OR ([user_type_id]=165)) AND ([sys].[all_columns].[max_length]=-1)
        THEN 2147483647
        -- hierarchyid geometry geography
        WHEN (([system_type_id]=240))
        THEN 2147483647
        -- xml
        WHEN (([system_type_id]=241) AND ([sys].[all_columns].[max_length]=-1))
        THEN 2147483647
        -- bit
        WHEN (([system_type_id]=104) OR ([user_type_id]=104))
        THEN 1/8    
        ELSE 
        CAST([sys].[all_columns].[max_length] AS BIGINT)
        END [max_length]
        , [sys].[all_columns].[is_nullable] AS [Is Nullable]
        , 
        CASE 
        WHEN EXISTS 
            (   
                SELECT type_desc FROM sys.indexes
                WHERE type_desc='CLUSTERED'
                AND [sys].[objects].[object_id]=[sys].[indexes].[object_id]
            )
        THEN 'CLUSTERED'
        ELSE 'HEAP'
        END AS [Table Type]
        , COUNT([sys].[all_columns].[name]) OVER (PARTITION BY [sys].[objects].[object_id]) AS [Total Number of Columns]
        ,SUM (CASE WHEN 
        -- varchar
        (
        (([system_type_id]=167) AND ([user_type_id]=167))
        OR
        -- nvarchar 
        (([system_type_id]=231) AND ([user_type_id]=231))
        )
        AND [sys].[all_columns].[is_nullable]=0
        THEN 1
        ELSE 0
        END) OVER (PARTITION BY [sys].[objects].[name]) AS [IsNonNullableVariableLength]
        ,SUM (
        CASE WHEN 
        -- varchar
        (([system_type_id]=167) OR ([user_type_id]=167))
        OR
        -- nvarchar 
        (([system_type_id]=231) OR ([user_type_id]=231))
        OR
        -- IMAGE
        (([system_type_id]=34) OR ([user_type_id]=34))
        OR
        -- TEXT
        (([system_type_id]=35) OR ([user_type_id]=35))
        OR 
        --  NTEXT
        (([system_type_id]=99) OR ([user_type_id]=99))
        --  VARBINARY
        OR
        (([system_type_id]=165) OR ([user_type_id]=165))
        OR 
        --  SQLVARIANT
        (([system_type_id]=98) OR ([user_type_id]=98))
        OR
        -- hierarchyid geometry geography
        (([system_type_id]=240))        
        OR
        -- xml
        (([system_type_id]=241))    
        THEN 1
        ELSE 0
        END) OVER (PARTITION BY [sys].[objects].[name]) 
        AS [IsVariableLength]
        ,SUM (
        CASE WHEN 
        -- varchar
        (([system_type_id]=167) OR ([user_type_id]=167))
        OR
        -- nvarchar 
        (([system_type_id]=231) OR ([user_type_id]=231))
        OR
        -- IMAGE
        (([system_type_id]=34) OR ([user_type_id]=34))
        OR
        -- TEXT
        (([system_type_id]=35) OR ([user_type_id]=35))
        OR 
        --  NTEXT
        (([system_type_id]=99) OR ([user_type_id]=99))
        --  VARBINARY
        OR
        (([system_type_id]=165) OR ([user_type_id]=165))
        OR 
        --  SQLVARIANT
        (([system_type_id]=98) OR ([user_type_id]=98))
        OR
        -- hierarchyid geometry geography
        (([system_type_id]=240))    
        OR
        -- xml
        (([system_type_id]=241))    
        THEN 0
        ELSE 1
        END) OVER (PARTITION BY [sys].[objects].[name]) 
        AS [AnyFixedColumn]
    FROM [sys].[objects]
    INNER JOIN sys.all_columns
    ON [sys].[objects].[object_id]=[sys].[all_columns].[object_id]
    WHERE type_desc='USER_TABLE'
    ) a1
    ) a2
Pavel Nefyodov
quelle
Nett. Ich denke, Sie sollten CEILING()beides tun SUM(...) AS [Max Size]und SUM(...) AS [Min Size]gebrochene BIT-Werte berücksichtigen (wenn die Anzahl der BIT-Felder nicht gleichmäßig durch 8 teilbar ist), da ein einzelnes BIT-Feld immer noch 1 Byte belegt. Auch irgendwelche Gedanken zur Berücksichtigung von: SPARSE, vardecimal und Komprimierung? Mir ist klar, dass es ziemlich kompliziert wäre, diese zu bearbeiten, aber ich bin gespannt, ob Annäherungen zumindest im Fall von SPARSE verwendet werden könnten, vielleicht auch vardezimal (veraltet, aber auch TEXT, IMAGE usw. :)
Solomon Rutzky,
@srutzky Guter Punkt, ich werde den BIT-Teil reparieren und das Skript aktualisieren. Dieses Skript wird als Teil eines größeren Skripts (gespeicherte Prozedur) verwendet, das die Größe der Tabellen in der Datenbank schätzt. vardecimal ist meines Erachtens kein Typ. Aber ich arbeite an anderen Verbesserungen.
Pavel Nefyodov
Ich habe nicht gesagt, dass dies VARDECIMALein Datentyp ist. Ich habe gesagt, dass dies zusammen mit der Option SPARSE und COMPRESSION die physische Speichergröße vieler Datentypen beeinflusst, die Sie korrekt berücksichtigen. Das heißt, ein BIGINTFeld, das als SPARSE NULL0 Byte oder 12 Byte markiert ist , nicht "immer 8 Byte". Es würde also eine Schätzung der Anzahl der Zeilen erfordern und wie viele davon NULL gegen NICHT NULL wären. Vielleicht ist es gut genug, weiterhin "immer 8" anzunehmen.
Solomon Rutzky