SUM des DATALENGTH ne correspondant pas à la taille de la table de sys.allocation_units


11

J'avais l'impression que si je sommais DATALENGTH()tous les champs pour tous les enregistrements d'une table, j'obtiendrais la taille totale de la table. Suis-je trompé?

SELECT 
SUM(DATALENGTH(Field1)) + 
SUM(DATALENGTH(Field2)) + 
SUM(DATALENGTH(Field3)) TotalSizeInBytes
FROM SomeTable
WHERE X, Y, and Z are true

J'ai utilisé cette requête ci-dessous (que j'ai obtenue en ligne pour obtenir les tailles de table, les index cluster uniquement afin qu'elle n'inclue pas les index NC) pour obtenir la taille d'une table particulière dans ma base de données. À des fins de facturation (nous facturons nos services en fonction de la quantité d'espace qu'ils utilisent), je dois déterminer la quantité d'espace utilisée par chaque service dans ce tableau. J'ai une requête qui identifie chaque groupe dans la table. J'ai juste besoin de comprendre combien d'espace chaque groupe occupe.

L'espace par ligne peut osciller énormément en raison des VARCHAR(MAX)champs dans la table, donc je ne peux pas simplement prendre une taille moyenne * le ratio de lignes pour un département. Lorsque j'utilise l' DATALENGTH()approche décrite ci-dessus, je n'obtiens que 85% de l'espace total utilisé dans la requête ci-dessous. Pensées?

SELECT 
s.Name AS SchemaName,
t.NAME AS TableName,
p.rows AS RowCounts,
(SUM(a.total_pages) * 8)/1024 AS TotalSpaceMB, 
(SUM(a.used_pages) * 8)/1024 AS UsedSpaceMB, 
((SUM(a.total_pages) - SUM(a.used_pages)) * 8)/1024 AS UnusedSpaceMB
FROM 
    sys.tables t with (nolock)
INNER JOIN 
    sys.schemas s with (nolock) ON s.schema_id = t.schema_id
INNER JOIN      
    sys.indexes i with (nolock) ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p with (nolock) ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a with (nolock) ON p.partition_id = a.container_id
WHERE 
    t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
    AND i.type_desc = 'Clustered'
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    TotalSpaceMB desc

Il a été suggéré de créer un index filtré pour chaque département ou de partitionner la table, afin de pouvoir interroger directement l'espace utilisé par index. Des index filtrés pourraient être créés par programme (et supprimés à nouveau pendant une fenêtre de maintenance ou lorsque j'ai besoin d'effectuer la facturation périodique), au lieu d'utiliser l'espace tout le temps (les partitions seraient meilleures à cet égard).

J'aime cette suggestion et je le fais généralement. Mais pour être honnête, j'utilise "chaque département" comme exemple pour expliquer pourquoi j'ai besoin de cela, mais pour être honnête, ce n'est pas vraiment pourquoi. Pour des raisons de confidentialité, je ne peux pas expliquer la raison exacte pour laquelle j'ai besoin de ces données, mais c'est analogue à différents départements.

Concernant les index non cluster sur cette table: Si je peux obtenir les tailles des index NC, ce serait génial. Cependant, les index NC représentent <1% de la taille de l'index clusterisé, nous sommes donc d'accord de ne pas les inclure. Cependant, comment pourrions-nous inclure les index NC de toute façon? Je ne peux même pas obtenir une taille précise pour l'index clusterisé :)


Donc, en substance, vous avez deux questions: (1) pourquoi la somme des longueurs de ligne ne correspond-elle pas à la comptabilité des métadonnées de la taille de la table entière? La réponse ci-dessous aborde cela au moins en partie (et qui peut varier selon la version et la fonctionnalité, par exemple la compression, le magasin de colonnes, etc.). Et plus important encore: (2) comment pouvez-vous déterminer avec précision l'espace réel utilisé par département? Je ne sais pas si vous pouvez le faire avec précision - car pour certaines des données expliquées dans la réponse, il n'y a aucun moyen de dire à quel département il appartient.
Aaron Bertrand

Je ne pense pas que le problème soit que vous n'ayez pas une taille précise pour l'index clusterisé - les métadonnées vous indiquent certainement avec précision la place que prend votre index. Ce que les métadonnées ne sont pas conçues pour vous dire - au moins compte tenu de votre conception / structure actuelle - combien de données sont associées à chaque département.
Aaron Bertrand

Réponses:


19

                          Please note that the following info is not intended to be a comprehensive
description of how data pages are laid out, such that one can calculate
the number of bytes used per any set of rows, as that is very complicated.

Les données ne sont pas la seule chose qui prend de la place sur une page de données 8k:

  • Il y a un espace réservé. Vous n'êtes autorisé à utiliser que 8060 des 8192 octets (c'est 132 octets qui ne vous ont jamais appartenu en premier lieu):

    • En-tête de page: c'est exactement 96 octets.
    • Tableau des emplacements: il s'agit de 2 octets par ligne et indique le décalage de l'endroit où chaque ligne commence sur la page. La taille de ce tableau n'est pas limitée aux 36 octets restants (132 - 96 = 36), sinon vous seriez effectivement limité à ne mettre que 18 lignes maximum sur une page de données. Cela signifie que chaque ligne est 2 octets plus grande que vous ne le pensez. Cette valeur n'est pas incluse dans la "taille d'enregistrement" telle que rapportée par DBCC PAGE, c'est pourquoi elle est conservée séparément ici au lieu d'être incluse dans les informations par ligne ci-dessous.
    • Métadonnées par ligne (y compris, mais sans s'y limiter):
      • La taille varie en fonction de la définition de la table (c'est-à-dire le nombre de colonnes, de longueur variable ou fixe, etc.). Informations tirées des commentaires de @ PaulWhite et @ Aaron qui peuvent être trouvées dans la discussion relative à cette réponse et à ces tests.
      • En-tête de ligne: 4 octets, 2 d'entre eux indiquant le type d'enregistrement, et les deux autres étant un décalage par rapport au bitmap NULL
      • Nombre de colonnes: 2 octets
      • Bitmap NULL: quelles colonnes sont actuellement NULL. 1 octet pour chaque ensemble de 8 colonnes. Et pour toutes les colonnes, même NOT NULLcelles. Par conséquent, au moins 1 octet.
      • Tableau de décalage de colonne de longueur variable: 4 octets minimum. 2 octets pour contenir le nombre de colonnes de longueur variable, puis 2 octets pour chaque colonne de longueur variable pour conserver le décalage à l'endroit où il commence.
      • Informations de version: 14 octets (ce sera présent si votre base de données est définie sur ALLOW_SNAPSHOT_ISOLATION ONou READ_COMMITTED_SNAPSHOT ON).
    • Veuillez consulter les questions et réponses suivantes pour plus de détails à ce sujet: baie de fentes et taille totale de la page
    • Veuillez consulter le billet de blog suivant de Paul Randall qui contient plusieurs détails intéressants sur la façon dont les pages de données sont présentées: Fouillez avec DBCC PAGE (Partie 1 de?)
  • Pointeurs LOB pour les données qui ne sont pas stockées en ligne. Cela représenterait donc DATALENGTH+ pointer_size. Mais ceux-ci ne sont pas de taille standard. Veuillez consulter le billet de blog suivant pour plus de détails sur ce sujet complexe: Quelle est la taille du pointeur LOB pour les types (MAX) comme Varchar, Varbinary, Etc? . Entre ce message lié et certains tests supplémentaires que j'ai effectués , les règles (par défaut) devraient être les suivantes:

    • Legacy / types LOB dépréciée que personne ne devrait être plus que de utilise SQL Server 2005 ( TEXT, NTEXTet IMAGE):
      • Par défaut, stockez toujours leurs données sur des pages LOB et utilisez toujours un pointeur de 16 octets vers le stockage LOB.
      • SI sp_tableoption a été utilisé pour définir l' text in rowoption, alors:
        • s'il y a de l'espace sur la page pour stocker la valeur et que la valeur n'est pas supérieure à la taille maximale en ligne (plage configurable de 24 à 7 000 octets avec une valeur par défaut de 256), elle sera stockée en ligne,
        • sinon ce sera un pointeur de 16 octets.
    • Pour les nouveaux types de LOB introduites dans SQL Server 2005 ( VARCHAR(MAX), NVARCHAR(MAX)et VARBINARY(MAX)):
      • Par défaut:
        • Si la valeur n'est pas supérieure à 8 000 octets et qu'il y a de la place sur la page, elle sera stockée en ligne.
        • Racine en ligne - pour les données comprises entre 8001 et 40000 (vraiment 42000) octets, si l'espace le permet, il y aura 1 à 5 pointeurs (24 à 72 octets) EN RANG qui pointent directement vers la ou les pages LOB. 24 octets pour la page LOB initiale de 8 Ko et 12 octets pour chaque page 8 Ko supplémentaire pour un maximum de quatre pages 8 Ko supplémentaires.
        • TEXT_TREE - pour les données de plus de 42 000 octets, ou si les 1 à 5 pointeurs ne peuvent pas tenir en ligne, alors il n'y aura qu'un pointeur de 24 octets vers la page de départ d'une liste de pointeurs vers les pages LOB (c'est-à-dire le "text_tree "page).
      • SI sp_tableoption a été utilisé pour définir l' large value types out of rowoption, utilisez toujours un pointeur de 16 octets vers le stockage LOB.
    • J'ai dit des règles "par défaut" parce que je n'ai pas testé les valeurs en ligne contre l'impact de certaines fonctionnalités telles que la compression des données, le chiffrement au niveau des colonnes, le chiffrement transparent des données, toujours chiffré, etc.
  • Pages de débordement LOB: si une valeur est de 10 ko, cela nécessitera 1 page complète de 8 ko de débordement, puis une partie d'une 2e page. Si aucune autre donnée ne peut occuper l'espace restant (ou y est même autorisée, je ne suis pas sûr de cette règle), alors vous avez environ 6 Ko d'espace "gaspillé" sur cette deuxième page de débordement de LOB.

  • Espace inutilisé: Une page de données de 8k est juste cela: 8192 octets. Il ne varie pas en taille. Les données et métadonnées qui y sont placées, cependant, ne s'intègrent pas toujours bien dans les 8192 octets. Et les lignes ne peuvent pas être divisées sur plusieurs pages de données. Donc, s'il vous reste 100 octets mais qu'aucune ligne (ou aucune ligne qui pourrait tenir à cet emplacement, en fonction de plusieurs facteurs) ne peut y tenir, la page de données occupe toujours 8192 octets, et votre deuxième requête ne compte que le nombre de pages de données. Vous pouvez trouver cette valeur à deux endroits (gardez juste à l'esprit qu'une partie de cette valeur est une certaine quantité de cet espace réservé):

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;Recherchez = "EN-TÊTE DE ParentObjectPAGE:" et Field= "m_freeCnt". Le Valuechamp est le nombre d'octets inutilisés.
    • SELECT buff.free_space_in_bytes FROM sys.dm_os_buffer_descriptors buff WHERE buff.[database_id] = DB_ID(N'db_name') AND buff.[page_id] = page_id;Il s'agit de la même valeur que celle signalée par "m_freeCnt". C'est plus facile que DBCC car il peut obtenir de nombreuses pages, mais nécessite également que les pages aient été lues dans le pool de tampons en premier lieu.
  • Espace réservé par FILLFACTOR<100. Les pages nouvellement créées ne respectent pas le FILLFACTORparamètre, mais effectuer une RECONSTRUCTION réservera cet espace sur chaque page de données. L'idée derrière l'espace réservé est qu'il sera utilisé par des insertions et / ou des mises à jour non séquentielles qui augmentent déjà la taille des lignes sur la page, en raison de la mise à jour des colonnes de longueur variable avec un peu plus de données (mais pas assez pour provoquer un page-split). Mais vous pouvez facilement réserver de l'espace sur des pages de données qui n'obtiendraient naturellement jamais de nouvelles lignes et n'auraient jamais les lignes existantes mises à jour, ou du moins pas mises à jour d'une manière qui augmenterait la taille de la ligne.

  • Page-Splits (fragmentation): La nécessité d'ajouter une ligne à un emplacement qui n'a pas de place pour la ligne entraînera une division de la page. Dans ce cas, environ 50% des données existantes sont déplacées vers une nouvelle page et la nouvelle ligne est ajoutée à l'une des 2 pages. Mais vous avez maintenant un peu plus d'espace libre qui n'est pas pris en compte par les DATALENGTHcalculs.

  • Lignes marquées pour suppression. Lorsque vous supprimez des lignes, elles ne sont pas toujours immédiatement supprimées de la page de données. S'ils ne peuvent pas être supprimés immédiatement, ils sont "marqués à mort" (référence Steven Segal) et seront physiquement supprimés plus tard par le processus de nettoyage des fantômes (je crois que c'est le nom). Cependant, ceux-ci pourraient ne pas être pertinents pour cette Question particulière.

  • Pages fantômes? Je ne sais pas si c'est le terme approprié, mais parfois les pages de données ne sont pas supprimées tant qu'une RECONSTRUCTION de l'index clusterisé n'est pas terminée. Cela représenterait également plus de pages qu'il n'en DATALENGTHfaudrait. Cela ne devrait généralement pas se produire, mais je l'ai rencontré une fois, il y a plusieurs années.

  • Colonnes SPARSE: les colonnes éparses économisent de l'espace (principalement pour les types de données de longueur fixe) dans les tables où un grand% des lignes sont NULLpour une ou plusieurs colonnes. L' SPARSEoption fait NULLmonter le type de valeur de 0 octet (au lieu de la quantité de longueur fixe normale, comme 4 octets pour un INT), mais les valeurs non NULL occupent chacune 4 octets supplémentaires pour les types de longueur fixe et une quantité variable pour types de longueur variable. Le problème ici est qu'il DATALENGTHn'inclut pas les 4 octets supplémentaires pour les valeurs non NULL dans une colonne SPARSE, donc ces 4 octets doivent être ajoutés à nouveau. Vous pouvez vérifier s'il y a des SPARSEcolonnes via:

    SELECT OBJECT_SCHEMA_NAME(sc.[object_id]) AS [SchemaName],
           OBJECT_NAME(sc.[object_id]) AS [TableName],
           sc.name AS [ColumnName]
    FROM   sys.columns sc
    WHERE  sc.is_sparse = 1;

    Et puis pour chaque SPARSEcolonne, mettez à jour la requête d'origine pour utiliser:

    SUM(DATALENGTH(FieldN) + 4)

    Veuillez noter que le calcul ci-dessus pour ajouter 4 octets standard est un peu simpliste car il ne fonctionne que pour les types de longueur fixe. ET, il y a des métadonnées supplémentaires par ligne (d'après ce que je peux dire jusqu'à présent) qui réduisent l'espace disponible pour les données, simplement en ayant au moins une colonne SPARSE. Pour plus de détails, veuillez consulter la page MSDN pour Utiliser des colonnes éparses .

  • Index et autres pages (par exemple IAM, PFS, GAM, SGAM, etc.): ce ne sont pas des pages "données" en termes de données utilisateur. Ceux-ci gonfleront la taille totale de la table. Si vous utilisez SQL Server 2012 ou une version plus récente, vous pouvez utiliser la sys.dm_db_database_page_allocationsfonction de gestion dynamique (DMF) pour voir les types de page (les versions antérieures de SQL Server peuvent utiliser DBCC IND(0, N'dbo.table_name', 0);):

    SELECT *
    FROM   sys.dm_db_database_page_allocations(
                   DB_ID(),
                   OBJECT_ID(N'dbo.table_name'),
                   1,
                   NULL,
                   N'DETAILED'
                  )
    WHERE  page_type = 1; -- DATA_PAGE

    Ni le DBCC INDni sys.dm_db_database_page_allocations(avec cette clause WHERE) ne signalera aucune page d'index, et seul le DBCC INDrapportera au moins une page IAM.

  • DATA_COMPRESSION: Si vous avez activé ROWou PAGECompression activé sur l'index cluster ou le tas, vous pouvez oublier la plupart de ce qui a été mentionné jusqu'à présent. L'en-tête de page de 96 octets, le tableau des emplacements de 2 octets par ligne et les informations de version de 14 octets par ligne sont toujours là, mais la représentation physique des données devient très complexe (beaucoup plus que ce qui a déjà été mentionné lors de la compression). n'est pas utilisé). Par exemple, avec la compression de lignes, SQL Server tente d'utiliser le plus petit conteneur possible pour s'adapter à chaque colonne, pour chaque ligne. Donc, si vous avez une BIGINTcolonne qui, autrement (en supposant qu'elle SPARSEn'est pas également activée), prendrait toujours 8 octets, si la valeur est comprise entre -128 et 127 (c'est-à-dire un entier signé de 8 bits), elle n'utilisera qu'un seul octet, et si le la valeur pourrait tenir dans unSMALLINT, il ne prendra que 2 octets. Les types entiers qui sont NULLou ne 0prennent pas d'espace et sont simplement indiqués comme étant NULLou "vides" (c'est-à-dire 0) dans un tableau mappant les colonnes. Et il y a beaucoup, beaucoup d'autres règles. Contiennent des données Unicode ( NCHAR, NVARCHAR(1 - 4000)mais pas NVARCHAR(MAX) , même si elles sont stockées en ligne)? La compression Unicode a été ajoutée dans SQL Server 2008 R2, mais il n'y a aucun moyen de prédire le résultat de la valeur "compressée" dans toutes les situations sans effectuer la compression réelle étant donné la complexité des règles .

Donc, en réalité, votre deuxième requête, bien que plus précise en termes d'espace physique total occupé sur le disque, n'est vraiment précise qu'en effectuant un REBUILDde l'index clusterisé. Et après cela, vous devez toujours tenir compte de tout FILLFACTORparamètre inférieur à 100. Et même dans ce cas, il y a toujours des en-têtes de page, et souvent suffisamment d'espace "gaspillé" qui n'est tout simplement pas remplissable car trop petit pour tenir dans une ligne de ce table, ou au moins la ligne qui devrait logiquement aller dans cet emplacement.

En ce qui concerne la précision de la 2e requête dans la détermination de l '"utilisation des données", il semble plus juste de sauvegarder les octets d'en-tête de page car ils ne sont pas des données: ils représentent des frais généraux liés au coût de l'entreprise. S'il y a 1 ligne sur une page de données et que cette ligne n'est qu'un TINYINT, alors cet octet exigeait toujours que la page de données existe et donc les 96 octets de l'en-tête. Ce service devrait-il être facturé pour toute la page de données? Si cette page de données est ensuite remplie par le ministère # 2, répartiraient-ils également ces frais généraux ou paieraient-ils proportionnellement? Il semble plus facile de le retirer. Dans ce cas, l'utilisation d'une valeur de 8pour se multiplier number of pagesest trop élevée. Que diriez-vous:

-- 8192 byte data page - 96 byte header = 8096 (approx) usable bytes.
SELECT 8060.0 / 1024 -- 7.906250

Par conséquent, utilisez quelque chose comme:

(SUM(a.total_pages) * 7.91) / 1024 AS [TotalSpaceMB]

pour tous les calculs par rapport aux colonnes "number_of_pages".

ET , considérant que l'utilisation DATALENGTHpar chaque champ ne peut pas renvoyer les métadonnées par ligne, qui doivent être ajoutées à votre requête par table où vous obtenez le DATALENGTHpar chaque champ, en filtrant sur chaque "département":

  • Type d'enregistrement et décalage vers NULL Bitmap: 4 octets
  • Nombre de colonnes: 2 octets
  • Slot Array: 2 octets (non inclus dans la "taille d'enregistrement" mais doivent toujours être pris en compte)
  • Bitmap NULL: 1 octet toutes les 8 colonnes (pour toutes les colonnes)
  • Version de ligne: 14 octets (si la base de données a soit ALLOW_SNAPSHOT_ISOLATIONou est READ_COMMITTED_SNAPSHOTdéfinie sur ON)
  • Tableau de décalage de colonne de longueur variable: 0 octet si toutes les colonnes sont de longueur fixe. Si des colonnes sont de longueur variable, alors 2 octets, plus 2 octets pour chacune des colonnes de longueur variable uniquement.
  • Pointeurs LOB: cette partie est très imprécise car il n'y aura pas de pointeur si la valeur est NULL, et si la valeur tient sur la ligne, elle peut être beaucoup plus petite ou beaucoup plus grande que le pointeur, et si la valeur est stockée hors- ligne, la taille du pointeur peut dépendre de la quantité de données. Cependant, puisque nous voulons juste une estimation (c'est-à-dire "swag"), il semble que 24 octets soit une bonne valeur à utiliser (enfin, aussi bonne que toute autre ;-). C'est pour chaque MAXchamp.

Par conséquent, utilisez quelque chose comme:

  • En général (en-tête de ligne + nombre de colonnes + tableau d'emplacements + bitmap NULL):

    ([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
  • En général (détection automatique si des "informations de version" sont présentes):

    + (SELECT CASE WHEN snapshot_isolation_state = 1 OR is_read_committed_snapshot_on = 1
                     THEN 14 ELSE 0 END FROM sys.databases WHERE [database_id] = DB_ID())
  • S'il existe des colonnes de longueur variable, ajoutez:

    + 2 + (2 * {NumVariableLengthColumns})
  • S'il y a des MAXcolonnes / LOB, ajoutez:

    + (24 * {NumLobColumns})
  • En général:

    )) AS [MetaDataBytes]

Ce n'est pas exact, et encore une fois ne fonctionnera pas si vous avez activé la compression de ligne ou de page sur le tas ou l'index clusterisé, mais devrait certainement vous rapprocher.


MISE À JOUR concernant le mystère de la différence de 15%

Nous (moi-même inclus) étions tellement concentrés sur la façon dont les pages de données sont disposées et sur la façon dont DATALENGTHpourraient expliquer les choses que nous n'avons pas passé beaucoup de temps à examiner la deuxième requête. J'ai exécuté cette requête sur une seule table, puis j'ai comparé ces valeurs à ce qui était signalé par sys.dm_db_database_page_allocationset ce n'étaient pas les mêmes valeurs pour le nombre de pages. Sur une intuition, j'ai supprimé les fonctions d'agrégation et GROUP BY, et remplacé la SELECTliste par a.*, '---' AS [---], p.*. Et puis, il est devenu clair: les gens doivent faire attention d'où sur ces interwebs troubles ils obtiennent leurs informations et leurs scripts ;-). La deuxième requête publiée dans la question n'est pas exactement correcte, en particulier pour cette question particulière.

  • Problème mineur: en dehors de cela n'a pas beaucoup de sens pour GROUP BY rows(et ne pas avoir cette colonne dans une fonction d'agrégation), le JOIN entre sys.allocation_unitset sys.partitionsn'est pas techniquement correct. Il existe 3 types d'unités d'allocation, et l'un d'entre eux doit se joindre à un champ différent. Assez souvent partition_idet hobt_idsont les mêmes, donc il pourrait ne jamais y avoir de problème, mais parfois ces deux champs ont des valeurs différentes.

  • Problème majeur: la requête utilise le used_pageschamp. Ce champ couvre tous les types de pages: données, index, IAM, etc., tc. Il y a un autre, champ plus approprié à utiliser lorsque concerné que les données réelles: data_pages.

J'ai adapté la 2e requête de la question en gardant à l'esprit les éléments ci-dessus et en utilisant la taille de la page de données qui sauvegarde l'en-tête de la page. J'ai aussi enlevé deux JOIN qui étaient inutiles: sys.schemas(remplacé par appel SCHEMA_NAME()), et sys.indexes(l'index cluster est toujours index_id = 1et nous avons index_iden sys.partitions).

SELECT  SCHEMA_NAME(st.[schema_id]) AS [SchemaName],
        st.[name] AS [TableName],
        SUM(sp.[rows]) AS [RowCount],
        (SUM(sau.[total_pages]) * 8.0) / 1024 AS [TotalSpaceMB],
        (SUM(CASE sau.[type]
           WHEN 1 THEN sau.[data_pages]
           ELSE (sau.[used_pages] - 1) -- back out the IAM page
         END) * 7.91) / 1024 AS [TotalActualDataMB]
FROM        sys.tables st
INNER JOIN  sys.partitions sp
        ON  sp.[object_id] = st.[object_id]
INNER JOIN  sys.allocation_units sau
        ON  (   sau.[type] = 1
            AND sau.[container_id] = sp.[partition_id]) -- IN_ROW_DATA
        OR  (   sau.[type] = 2
            AND sau.[container_id] = sp.[hobt_id]) -- LOB_DATA
        OR  (   sau.[type] = 3
            AND sau.[container_id] = sp.[partition_id]) -- ROW_OVERFLOW_DATA
WHERE       st.is_ms_shipped = 0
--AND         sp.[object_id] = OBJECT_ID(N'dbo.table_name')
AND         sp.[index_id] < 2 -- 1 = Clustered Index; 0 = Heap
GROUP BY    SCHEMA_NAME(st.[schema_id]), st.[name]
ORDER BY    [TotalSpaceMB] DESC;

Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Paul White 9

Bien que la requête mise à jour que vous avez fournie pour la deuxième requête soit encore plus éloignée (dans l'autre sens maintenant :)), je suis d'accord avec cette réponse. C'est un écrou très difficile à casser apparemment et pour ce que ça vaut, je suis content même avec les experts qui m'ont aidé, je n'ai toujours pas pu comprendre la raison exacte pour laquelle les deux méthodes ne correspondent pas. Je vais simplement utiliser la méthodologie de l'autre réponse pour extrapoler. J'aimerais pouvoir voter oui pour ces deux réponses, mais @srutzky a aidé avec toutes les raisons pour lesquelles les deux seraient désactivées.
Chris Woods

6

C'est peut-être une réponse grunge, mais c'est ce que je ferais.

La DATALENGTH ne représente donc que 86% du total. C'est encore une division très représentative. Les frais généraux dans l'excellente réponse de srutzky devraient avoir une répartition assez uniforme.

J'utiliserais votre deuxième requête (pages) pour le total. Et utilisez la première (longueur de données) pour allouer le partage. De nombreux coûts sont répartis en utilisant une normalisation.

Et vous devez considérer qu'une réponse plus proche va augmenter les coûts, donc même le département qui a perdu sur une scission peut toujours payer plus.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.