SOMMA di DATALENGTH che non corrispondono alle dimensioni della tabella da sys.allocation_units


11

Avevo l'impressione che se dovessi sommare DATALENGTH()tutti i campi per tutti i record in una tabella otterrei la dimensione totale della tabella. Mi sbaglio?

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

Ho usato questa query di seguito (che ho ricevuto online per ottenere dimensioni di tabella, indici cluster solo in modo che non includa indici NC) per ottenere la dimensione di una tabella particolare nel mio database. Ai fini della fatturazione (addebitiamo ai nostri reparti la quantità di spazio che utilizzano) Devo capire quanto spazio ogni reparto ha utilizzato in questa tabella. Ho una query che identifica ogni gruppo all'interno della tabella. Devo solo capire quanto spazio occupa ogni gruppo.

Lo spazio per riga può oscillare selvaggiamente a causa dei VARCHAR(MAX)campi nella tabella, quindi non posso semplicemente prendere una dimensione media * il rapporto delle righe per un reparto. Quando utilizzo l' DATALENGTH()approccio sopra descritto ottengo solo l'85% dello spazio totale utilizzato nella query di seguito. Pensieri?

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

È stato suggerito di creare un indice filtrato per ciascun reparto o di partizionare la tabella, in modo da poter eseguire una query diretta sullo spazio utilizzato per indice. Gli indici filtrati potrebbero essere creati in modo programmatico (e rilasciati di nuovo durante una finestra di manutenzione o quando devo eseguire la fatturazione periodica), invece di utilizzare lo spazio tutto il tempo (le partizioni sarebbero migliori in questo senso).

Mi piace quel suggerimento e in genere lo farei. Ma ad essere sincero uso l'esempio "ogni reparto" per spiegare perché ne ho bisogno, ma ad essere sincero, non è proprio per questo. Per motivi di riservatezza non posso spiegare il motivo esatto per cui ho bisogno di questi dati, ma è analogo a diversi dipartimenti.

Per quanto riguarda gli indici non cluster in questa tabella: se riesco a ottenere le dimensioni degli indici NC, sarebbe fantastico. Tuttavia, gli indici NC rappresentano <1% della dimensione dell'indice cluster, quindi siamo d'accordo non includerli. Tuttavia, come includeremo comunque gli indici NC? Non riesco nemmeno a ottenere una dimensione precisa per l'indice cluster :)


Quindi, in sostanza, hai due domande: (1) perché la somma delle lunghezze delle righe non corrisponde alla contabilità dei metadati della dimensione dell'intera tabella? La risposta che segue affronta almeno in parte (e che può variare in base al rilascio e per funzione, ad esempio compressione, archivio colonne, ecc.). E ancora più importante: (2) come è possibile determinare con precisione lo spazio effettivo utilizzato per reparto? Non so che puoi farlo con precisione, perché per alcuni dei dati spiegati nella risposta, non c'è modo di dire a quale reparto appartiene.
Aaron Bertrand

Non penso che il problema sia che non hai una dimensione precisa per l'indice cluster - i metadati ti dicono sicuramente con precisione quanto spazio occupa il tuo indice. Che cosa i metadati non sono progettati per dirti - almeno dato il tuo attuale design / struttura - quanti dati sono associati a ciascun dipartimento.
Aaron Bertrand

Risposte:


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.

I dati non sono l'unica cosa che occupa spazio su una pagina di dati 8k:

  • C'è spazio riservato. Puoi utilizzare solo 8060 degli 8192 byte (ovvero 132 byte che non sono mai stati tuoi in primo luogo):

    • Intestazione di pagina: esattamente 96 byte.
    • Matrice slot: si tratta di 2 byte per riga e indica l'offset del punto in cui ogni riga inizia sulla pagina. La dimensione di questo array non è limitata ai restanti 36 byte (132 - 96 = 36), altrimenti si sarebbe effettivamente limitati a mettere solo 18 righe al massimo in una pagina di dati. Ciò significa che ogni riga ha 2 byte in più di quanto si pensi. Questo valore non è incluso nella "dimensione del record" come riportato da DBCC PAGE, motivo per cui è tenuto separato qui invece di essere incluso nelle informazioni per riga di seguito.
    • Meta-dati per riga (inclusi, ma non limitati a):
      • Le dimensioni variano in base alla definizione della tabella (ad es. Numero di colonne, lunghezza variabile o lunghezza fissa, ecc.). Informazioni tratte dai commenti di @ PaulWhite e @ Aaron che possono essere trovati nella discussione relativa a questa risposta e test.
      • Row-header: 4 byte, 2 dei quali indicano il tipo di record e gli altri due sono un offset rispetto alla bitmap NULL
      • Numero di colonne: 2 byte
      • NULL Bitmap: quali colonne sono attualmente NULL. 1 byte per ogni set di 8 colonne. E per tutte le colonne, anche NOT NULLquelle. Quindi, minimo 1 byte.
      • Matrice offset a colonna a lunghezza variabile: minimo 4 byte. 2 byte per contenere il numero di colonne di lunghezza variabile, quindi 2 byte per ogni colonna di lunghezza variabile per mantenere l'offset rispetto al punto di inizio.
      • Informazioni sulla versione: 14 byte (sarà presente se il database è impostato su ALLOW_SNAPSHOT_ISOLATION ONo READ_COMMITTED_SNAPSHOT ON).
    • Per ulteriori dettagli, consultare la seguente domanda e risposta: Matrice di slot e Dimensione pagina totale
    • Si prega di consultare il seguente post sul blog di Paul Randall che contiene diversi dettagli interessanti su come sono disposte le pagine di dati: Cercando con DBCC PAGE (Parte 1 di?)
  • Puntatori LOB per dati che non sono memorizzati in riga. Quindi ciò rappresenterebbe DATALENGTH+ pointer_size. Ma questi non sono di dimensioni standard. Per i dettagli su questo argomento complesso, consultare il seguente post di blog: Qual è la dimensione del puntatore LOB per tipi (MAX) come Varchar, Varbinary, Etc? . Tra quel post collegato e alcuni test aggiuntivi che ho fatto , le regole (predefinite) dovrebbero essere le seguenti:

    • Legacy / deprecato tipi LOB che nessuno dovrebbe usare più come di SQL Server 2005 ( TEXT, NTEXT, e IMAGE):
      • Per impostazione predefinita, archiviare sempre i propri dati su pagine LOB e utilizzare sempre un puntatore a 16 byte per l'archiviazione LOB.
      • Se sp_tableoption è stato utilizzato per impostare l' text in rowopzione, quindi:
        • se nella pagina c'è spazio per memorizzare il valore e il valore non è maggiore della dimensione massima nella riga (intervallo configurabile di 24 - 7000 byte con un valore predefinito di 256), verrà archiviato nella riga,
        • altrimenti sarà un puntatore a 16 byte.
    • Per i tipi LOB più recenti introdotte in SQL Server 2005 ( VARCHAR(MAX), NVARCHAR(MAX), e VARBINARY(MAX)):
      • Di default:
        • Se il valore non è maggiore di 8000 byte e c'è spazio nella pagina, verrà archiviato in riga.
        • Radice incorporata: per i dati tra 8001 e 40.000 (in realtà 42.000) byte, spazio consentito, ci saranno da 1 a 5 puntatori (24 - 72 byte) IN RIGA che puntano direttamente alle pagine LOB. 24 byte per la pagina iniziale 8k LOB e 12 byte per ogni pagina aggiuntiva 8k per un massimo di altre quattro pagine 8k.
        • TEXT_TREE - per i dati superiori a 42.000 byte o se i puntatori da 1 a 5 non possono adattarsi in fila, allora ci sarà solo un puntatore a 24 byte alla pagina iniziale di un elenco di puntatori alle pagine LOB (ovvero "text_tree " pagina).
      • Se sp_tableoption è stato utilizzato per impostare l' large value types out of rowopzione, quindi utilizzare sempre un puntatore a 16 byte per l'archiviazione LOB.
    • Ho detto regole "predefinite" perché non ho testato i valori in fila contro l'impatto di alcune funzionalità come Compressione dati, Crittografia a livello di colonna, Crittografia dati trasparente, Crittografia sempre, ecc.
  • Pagine di overflow LOB: se un valore è 10k, ciò richiederà 1 pagina di overflow 8k completa e quindi parte di una seconda pagina. Se nessun altro dato può occupare lo spazio rimanente (o è anche permesso, non sono sicuro di quella regola), allora hai circa 6kb di spazio "sprecato" su quella seconda pagina di dati di overflow del LOB.

  • Spazio inutilizzato: una pagina di dati 8k è proprio questa: 8192 byte. Non varia di dimensioni. I dati e i metadati posti su di esso, tuttavia, non sempre si adattano perfettamente a tutti gli 8192 byte. E le righe non possono essere suddivise su più pagine di dati. Quindi, se hai ancora 100 byte ma nessuna riga (o nessuna riga che si adatterebbe in quella posizione, a seconda di diversi fattori) può adattarsi lì, la pagina di dati sta ancora occupando 8192 byte e la tua seconda query conta solo il numero di pagine di dati. Puoi trovare questo valore in due punti (tieni presente che una parte di questo valore è una quantità di quello spazio riservato):

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;Cerca ParentObject= "PAGE HEADER:" e Field= "m_freeCnt". Il Valuecampo è il numero di byte non utilizzati.
    • 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;Questo è lo stesso valore riportato da "m_freeCnt". Questo è più semplice di DBCC poiché può ottenere molte pagine, ma richiede anche che le pagine siano state lette nel pool di buffer in primo luogo.
  • Spazio riservato da FILLFACTOR<100. Le pagine appena create non rispettano l' FILLFACTORimpostazione, ma facendo un REVISION si riserva lo spazio su ciascuna pagina di dati. L'idea alla base dello spazio riservato è che verrà utilizzato da inserimenti e / o aggiornamenti non sequenziali che espandono già la dimensione delle righe nella pagina, a causa dell'aggiornamento delle colonne a lunghezza variabile con un numero leggermente maggiore di dati (ma non abbastanza per causare un pagina-split). Ma potresti facilmente riservare spazio su pagine di dati che naturalmente non otterrebbero mai nuove righe e non avrebbero mai aggiornato le righe esistenti, o almeno non aggiornate in modo da aumentare le dimensioni della riga.

  • Divisioni di pagina (frammentazione): la necessità di aggiungere una riga in una posizione che non ha spazio per la riga causerà una divisione della pagina. In questo caso, circa il 50% dei dati esistenti viene spostato in una nuova pagina e la nuova riga viene aggiunta a una delle 2 pagine. Ma ora hai un po 'più di spazio libero che non è rappresentato dai DATALENGTHcalcoli.

  • Righe contrassegnate per l'eliminazione. Quando si eliminano le righe, queste non vengono sempre rimosse immediatamente dalla pagina dei dati. Se non possono essere rimossi immediatamente, vengono "contrassegnati per la morte" (riferimento a Steven Segal) e verranno rimossi fisicamente in seguito dal processo di pulizia dei fantasmi (credo che sia il nome). Tuttavia, questi potrebbero non essere rilevanti per questa particolare domanda.

  • Pagine fantasma? Non sono sicuro se questo sia il termine corretto, ma a volte le pagine di dati non vengono rimosse fino a quando non viene eseguita una RICOSTRUZIONE dell'indice cluster. Ciò spiegherebbe anche più pagine di quante DATALENGTHne aggiungerebbe. Questo in genere non dovrebbe accadere, ma l'ho incontrato una volta, diversi anni fa.

  • Colonne SPARSE: le colonne sparse risparmiano spazio (principalmente per tipi di dati a lunghezza fissa) nelle tabelle in cui una grande percentuale delle righe è NULLper una o più colonne. L' SPARSEopzione fa in modo che il NULLvalore comporti 0 byte (anziché la normale quantità a lunghezza fissa, come 4 byte per un INT), ma i valori non NULL occupano ciascuno 4 byte aggiuntivi per tipi a lunghezza fissa e un importo variabile per tipi a lunghezza variabile. Il problema qui è che DATALENGTHnon include i 4 byte extra per i valori non NULL in una colonna SPARSE, quindi è necessario aggiungere nuovamente quei 4 byte. Puoi verificare se ci sono SPARSEcolonne tramite:

    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;
    

    E quindi per ogni SPARSEcolonna, aggiorna la query originale per utilizzare:

    SUM(DATALENGTH(FieldN) + 4)

    Si noti che il calcolo sopra riportato per aggiungere uno standard di 4 byte è un po 'semplicistico in quanto funziona solo per tipi a lunghezza fissa. E, ci sono ulteriori metadati per riga (da quello che posso dire finora) che riducono lo spazio disponibile per i dati, semplicemente avendo almeno una colonna SPARSE. Per ulteriori dettagli, consultare la pagina MSDN per Usa colonne sparse .

  • Indice e altre pagine (es. IAM, PFS, GAM, SGAM, ecc.): Queste non sono pagine di "dati" in termini di dati dell'utente. Questi aumenteranno la dimensione totale della tabella. Se si utilizza SQL Server 2012 o versioni successive, è possibile utilizzare la sys.dm_db_database_page_allocationsfunzione di gestione dinamica (DMF) per visualizzare i tipi di pagina ( è possibile utilizzare le versioni precedenti di SQL Server 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
    

    Né il DBCC INDsys.dm_db_database_page_allocations(con quella clausola WHERE) segnalerà alcuna pagina dell'Indice e solo la DBCC INDsegnalerà almeno una pagina IAM.

  • DATA_COMPRESSION: se hai attivato ROWo la PAGEcompressione sull'indice cluster o heap, puoi dimenticare la maggior parte di ciò che è stato menzionato finora. L'intestazione della pagina a 96 byte, l'array di slot da 2 byte per riga e le informazioni sulla versione di 14 byte per riga sono ancora presenti, ma la rappresentazione fisica dei dati diventa estremamente complessa (molto più di quanto è già stato menzionato durante la compressione non viene utilizzato). Ad esempio, con la compressione di riga, SQL Server tenta di utilizzare il contenitore più piccolo possibile per adattarsi a ciascuna colonna, per ogni riga. Quindi, se hai una BIGINTcolonna che altrimenti (supponendo che SPARSEnon sia abilitata) occuperebbe sempre 8 byte, se il valore è compreso tra -128 e 127 (cioè un intero a 8 bit con segno), utilizzerà solo 1 byte e se il il valore potrebbe rientrare in aSMALLINT, richiederà solo 2 byte. Tipi interi che sono NULLo 0non occupano spazio e vengono semplicemente indicati come NULL"vuoti" (ovvero 0) in un array che traccia le colonne. E ci sono molte, molte altre regole. Hai dati Unicode ( NCHAR, NVARCHAR(1 - 4000)ma non NVARCHAR(MAX) , anche se archiviati in fila)? La compressione Unicode è stata aggiunta in SQL Server 2008 R2, ma non è possibile prevedere l'esito del valore "compresso" in tutte le situazioni senza eseguire la compressione effettiva data la complessità delle regole .

Quindi, davvero, la tua seconda query, sebbene più accurata in termini di spazio fisico totale occupato su disco, è davvero accurata solo quando si esegue un REBUILDindice cluster. FILLFACTORDopodiché , devi comunque tenere conto di qualsiasi impostazione inferiore a 100. E anche allora ci sono sempre intestazioni di pagina, e spesso abbastanza quantità di spazio "sprecato" che non è semplicemente compilabile a causa dell'essere troppo piccolo per adattarsi a qualsiasi riga in questo tabella, o almeno la riga che logicamente dovrebbe andare in quello slot.

Per quanto riguarda l'accuratezza della seconda query nel determinare "utilizzo dei dati", sembra più corretto eseguire il back-out dei byte dell'intestazione della pagina poiché non sono un utilizzo dei dati: sono costi generali di business. Se c'è una riga su una pagina di dati e quella riga è solo una TINYINT, allora quel 1 byte richiede comunque che la pagina di dati esista e quindi i 96 byte dell'intestazione. Quel reparto dovrebbe essere addebitato per l'intera pagina di dati? Se la pagina dei dati fosse quindi riempita dal Dipartimento n. 2, dividerebbero uniformemente quel costo "generale" o pagherebbero in modo proporzionale? Sembra più semplice semplicemente ripristinarlo. In tal caso, l'utilizzo di un valore 8da moltiplicare per number of pagesè troppo alto. Che ne dite di:

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

Quindi, usa qualcosa come:

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

per tutti i calcoli rispetto alle colonne "number_of_pages".

E , considerando che l'utilizzo DATALENGTHper ciascun campo non può restituire i metadati per riga, che dovrebbe essere aggiunto alla query per tabella in cui si ottiene il DATALENGTHcampo per ciascun campo, filtrando su ciascun "reparto":

  • Tipo di record e offset su NULL Bitmap: 4 byte
  • Numero di colonne: 2 byte
  • Matrice di slot: 2 byte (non inclusa nella "dimensione del record" ma deve comunque essere considerata)
  • Bitmap NULL: 1 byte ogni 8 colonne (per tutte le colonne)
  • Row Versioning: 14 byte (se il database ha ALLOW_SNAPSHOT_ISOLATIONo READ_COMMITTED_SNAPSHOTimpostato su ON)
  • Matrice offset a colonna a lunghezza variabile: 0 byte se tutte le colonne sono a lunghezza fissa. Se le colonne sono di lunghezza variabile, quindi 2 byte, più 2 byte per ciascuna delle sole colonne di lunghezza variabile.
  • Puntatori LOB: questa parte è molto imprecisa poiché non ci sarà un puntatore se il valore è NULL, e se il valore si adatta alla riga, allora può essere molto più piccolo o molto più grande del puntatore e se il valore è archiviato off- riga, quindi la dimensione del puntatore potrebbe dipendere dalla quantità di dati presenti. Tuttavia, poiché vogliamo solo una stima (ovvero "swag"), sembra che 24 byte sia un buon valore da usare (beh, buono come qualsiasi altro ;-). Questo è per ogni MAXcampo.

Quindi, usa qualcosa come:

  • In generale (intestazione di riga + numero di colonne + array di slot + bitmap NULL):

    ([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
  • In generale (rileva automaticamente se sono presenti "informazioni sulla versione"):

    + (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())
  • SE ci sono colonne di lunghezza variabile, quindi aggiungi:

    + 2 + (2 * {NumVariableLengthColumns})
  • SE ci sono MAXcolonne / LOB, quindi aggiungi:

    + (24 * {NumLobColumns})
  • In generale:

    )) AS [MetaDataBytes]

Ciò non è esatto e, di nuovo, non funzionerà se è stata abilitata la compressione di riga o di pagina nell'heap o nell'indice cluster, ma dovrebbe sicuramente avvicinarti.


AGGIORNAMENTO per quanto riguarda il mistero delle differenze del 15%

Noi (incluso me stesso) eravamo così concentrati nel pensare a come sono disposte le pagine di dati e su come DATALENGTHpotrebbero spiegare cose che non abbiamo trascorso molto tempo a rivedere la seconda query. Ho eseguito quella query su una singola tabella e quindi ho confrontato quei valori con ciò che veniva segnalato sys.dm_db_database_page_allocationse non erano gli stessi valori per il numero di pagine. In un sospetto, ho rimosso le funzioni di aggregazione e ho GROUP BYsostituito l' SELECTelenco con a.*, '---' AS [---], p.*. E poi è diventato chiaro: le persone devono stare attenti da dove su queste oscure interwebs ottengono le loro informazioni e script ;-). La seconda query pubblicata nella domanda non è esattamente corretta, specialmente per questa particolare domanda.

  • Problema minore: al di fuori di esso non ha molto senso GROUP BY rows(e non ha quella colonna in una funzione aggregata), il JOIN tra sys.allocation_unitse sys.partitionsnon è tecnicamente corretto. Esistono 3 tipi di unità di allocazione e una di esse dovrebbe ISCRIVERSI a un altro campo. Abbastanza spesso partition_ide hobt_idsono uguali, quindi potrebbe non esserci mai un problema, ma a volte quei due campi hanno valori diversi.

  • Problema principale: la query utilizza il used_pagescampo. Quel campo copre tutti i tipi di pagine: dati, indice, IAM, ecc., Tc. V'è un altro, il campo più appropriato da utilizzare quando si occupa di solo i dati effettivi: data_pages.

Ho adattato la seconda query nella domanda tenendo presente gli elementi precedenti e utilizzando la dimensione della pagina di dati che esegue il backout dell'intestazione della pagina. Ho anche rimosso due join che erano inutili: sys.schemas(sostituito con chiamata a SCHEMA_NAME()), e sys.indexes(l'indice cluster è sempre index_id = 1e noi abbiamo index_idin 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;

I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Paul White 9

Sebbene la query aggiornata che hai fornito per la seconda query sia ancora più lontana (nell'altra direzione ora :)), sono d'accordo con questa risposta. Questo è un dado molto difficile da rompere apparentemente e per quello che vale, sono contento anche con gli esperti che mi aiutano a non essere ancora in grado di capire il motivo esatto per cui i due metodi non corrispondono. Userò solo la metodologia nell'altra risposta per estrapolare. Vorrei poter votare sì per entrambe queste risposte, ma @srutzky ha aiutato con tutti i motivi per cui i due sarebbero stati esclusi.
Chris Woods,

6

Forse questa è una risposta grunge ma questo è quello che vorrei fare.

Quindi DATALENGTH rappresenta solo l'86% del totale. È ancora una divisione molto rappresentativa. Il sovraccarico nell'eccellente risposta di srutzky dovrebbe avere una divisione abbastanza uniforme.

Vorrei utilizzare la tua seconda query (pagine) per il totale. E usa il primo (lunghezza dati) per allocare la divisione. Molti costi sono assegnati usando una normalizzazione.

E devi considerare che una risposta più vicina aumenterà i costi, quindi anche il reparto che ha perso in una divisione può ancora pagare di più.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.