SUM dari DATALENGTHs tidak cocok dengan ukuran tabel dari sys.allocation_units

11

Saya mendapat kesan bahwa jika saya menjumlahkan DATALENGTH()semua bidang untuk semua catatan dalam tabel, saya akan mendapatkan ukuran total tabel. Apakah saya salah?

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

Saya menggunakan kueri ini di bawah ini (yang saya dapatkan dari online untuk mendapatkan ukuran tabel, hanya indeks berkerumun sehingga tidak termasuk indeks NC) untuk mendapatkan ukuran tabel tertentu dalam database saya. Untuk tujuan penagihan (kami menagih departemen kami dengan jumlah ruang yang mereka gunakan) Saya perlu mencari tahu berapa banyak ruang yang digunakan masing-masing departemen dalam tabel ini. Saya memiliki kueri yang mengidentifikasi setiap grup di dalam tabel. Saya hanya perlu mencari tahu berapa banyak ruang yang digunakan oleh masing-masing kelompok.

Ruang per baris dapat berayun liar karena VARCHAR(MAX)bidang dalam tabel, jadi saya tidak bisa hanya mengambil ukuran rata-rata * rasio baris untuk suatu departemen. Ketika saya menggunakan DATALENGTH()pendekatan yang dijelaskan di atas, saya hanya mendapatkan 85% dari total ruang yang digunakan dalam kueri di bawah ini. Pikiran?

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

Disarankan agar saya membuat indeks yang difilter untuk setiap departemen atau partisi tabel, jadi saya bisa langsung meminta ruang yang digunakan per indeks. Indeks yang difilter dapat dibuat secara programatik (dan dijatuhkan lagi selama jendela pemeliharaan atau ketika saya perlu melakukan penagihan berkala), alih-alih menggunakan spasi sepanjang waktu (partisi akan lebih baik dalam hal ini).

Saya suka saran itu dan biasanya akan melakukannya. Tapi jujur ​​saja saya menggunakan "masing-masing dept" sebagai contoh untuk menjelaskan mengapa saya membutuhkan ini, tapi jujur, itu tidak benar-benar mengapa. Karena alasan kerahasiaan, saya tidak dapat menjelaskan alasan pasti mengapa saya membutuhkan data ini, tetapi ini analog dengan departemen yang berbeda.

Mengenai indeks nonclustered pada tabel ini: Jika saya bisa mendapatkan ukuran indeks NC, itu akan bagus. Namun, indeks NC menyumbang <1% ukuran indeks berkerumun, jadi kami tidak termasuk. Namun, bagaimana kita memasukkan indeks NC? Saya bahkan tidak bisa mendapatkan ukuran yang akurat untuk indeks Clustered :)

Chris Woods
sumber
Jadi pada dasarnya, Anda memiliki dua pertanyaan: (1) mengapa jumlah panjang baris tidak cocok dengan akuntansi metadata tentang ukuran seluruh tabel? Jawaban di bawah ini membahas bahwa setidaknya sebagian (dan yang dapat berfluktuasi per rilis dan per fitur, misalnya kompresi, kolom kolom, dll). Dan yang lebih penting: (2) bagaimana Anda bisa secara akurat menentukan ruang aktual yang digunakan per departemen? Saya tidak tahu bahwa Anda dapat melakukannya dengan akurat - karena untuk sebagian data yang diperhitungkan dalam jawaban, tidak ada cara untuk mengetahui departemen mana yang menjadi miliknya.
Aaron Bertrand
Saya tidak berpikir masalahnya adalah Anda tidak memiliki ukuran yang akurat untuk indeks berkerumun - metadata pasti memberi tahu Anda dengan tepat berapa banyak ruang yang digunakan indeks Anda. Apa metadata tidak dirancang untuk memberi tahu Anda - setidaknya mengingat desain / struktur Anda saat ini - berapa banyak data yang terkait dengan masing-masing departemen.
Aaron Bertrand

Jawaban:

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.

Data bukan satu-satunya hal yang menggunakan ruang pada halaman data 8k:

  • Ada ruang yang dipesan. Anda hanya diperbolehkan menggunakan 8060 dari 8192 byte (pertama, 132 byte yang tidak pernah menjadi milik Anda):

    • Header halaman: Ini persis 96 byte.
    • Slot array: ini adalah 2 byte per baris dan menunjukkan offset tempat setiap baris dimulai pada halaman. Ukuran array ini tidak terbatas pada 36 byte yang tersisa (132 - 96 = 36), jika tidak, Anda akan dibatasi secara efektif hanya dengan menempatkan 18 baris maks pada halaman data. Ini berarti bahwa setiap baris berukuran 2 byte lebih besar dari yang Anda kira. Nilai ini tidak termasuk dalam "ukuran catatan" seperti yang dilaporkan oleh DBCC PAGE, oleh karena itu disimpan terpisah di sini daripada dimasukkan dalam info per-baris di bawah ini.
    • Meta-data Per-Baris (termasuk, tetapi tidak terbatas pada):
      • Ukurannya bervariasi tergantung pada definisi tabel (yaitu jumlah kolom, panjang variabel atau panjang tetap, dll). Info diambil dari komentar @ PaulWhite dan @ Harun yang dapat ditemukan dalam diskusi terkait dengan jawaban dan pengujian ini.
      • Head-row: 4 byte, 2 di antaranya menunjukkan tipe record, dan dua lainnya merupakan offset ke NULL Bitmap
      • Jumlah kolom: 2 byte
      • Bitmap NULL: kolom mana yang saat ini NULL. 1 byte per setiap set 8 kolom. Dan untuk semua kolom, bahkan kolom NOT NULL. Oleh karena itu, minimal 1 byte.
      • Array offset kolom panjang variabel: minimum 4 byte. 2 byte untuk menahan jumlah kolom panjang variabel, dan kemudian 2 byte per setiap kolom panjang variabel untuk menahan offset ke tempat dimulai.
      • Info Versi: 14 byte (ini akan hadir jika database Anda diatur ke salah satu ALLOW_SNAPSHOT_ISOLATION ONatau READ_COMMITTED_SNAPSHOT ON).
    • Silakan lihat Pertanyaan dan Jawaban berikut untuk detail lebih lanjut tentang ini: Slot Array dan Ukuran Halaman Total
    • Silakan lihat posting blog berikut dari Paul Randall yang memiliki beberapa detail menarik tentang bagaimana halaman data disusun: Menyodok dengan DBCC PAGE (Bagian 1 dari?)
  • Pointer LOB untuk data yang tidak disimpan dalam baris. Jadi itu akan menjelaskan DATALENGTH+ pointer_size. Tapi ini bukan ukuran standar. Silakan lihat posting blog berikut untuk detail tentang topik kompleks ini: Berapa Ukuran Pointer LOB untuk (MAX) Jenis Seperti Varchar, Varbinary, Etc? . Di antara pos tertaut dan beberapa pengujian tambahan yang telah saya lakukan , aturan (default) adalah sebagai berikut:

    • Legacy / usang jenis LOB bahwa tak seorang pun harus menggunakan lagi karena dari SQL Server 2005 ( TEXT, NTEXT, dan IMAGE):
      • Secara default, selalu simpan datanya di halaman LOB dan selalu gunakan pointer 16 byte ke penyimpanan LOB.
      • JIKA sp_tableoption digunakan untuk mengatur text in rowopsi, maka:
        • jika ada ruang pada halaman untuk menyimpan nilai, dan nilainya tidak lebih besar dari ukuran maks dalam baris (kisaran yang dapat dikonfigurasi dari 24 - 7000 byte dengan standar 256), maka itu akan disimpan dalam baris,
        • selain itu akan menjadi pointer 16-byte.
    • Untuk jenis LOB baru diperkenalkan di SQL Server 2005 ( VARCHAR(MAX), NVARCHAR(MAX), dan VARBINARY(MAX)):
      • Secara default:
        • Jika nilainya tidak lebih dari 8000 byte, dan ada ruang pada halaman, maka akan disimpan secara baris.
        • Inline Root - untuk data antara 8001 dan 40.000 (benar-benar 42.000) byte, ruang mengizinkan, akan ada 1 hingga 5 pointer (24 - 72 byte) DALAM ROW yang mengarah langsung ke halaman LOB. 24 byte untuk halaman 8k LOB awal, dan 12 byte per setiap halaman 8k tambahan hingga empat halaman 8k lebih.
        • TEXT_TREE - untuk data lebih dari 42.000 byte, atau jika 1 hingga 5 pointer tidak dapat dimasukkan secara berturut-turut, maka hanya akan ada pointer 24 byte ke halaman awal daftar pointer ke halaman LOB (yaitu "text_tree" "halaman).
      • JIKA sp_tableoption digunakan untuk mengatur large value types out of rowopsi, maka selalu gunakan pointer 16 byte ke penyimpanan LOB.
    • Saya mengatakan aturan "default" karena saya tidak menguji nilai dalam baris terhadap dampak fitur tertentu seperti Kompresi Data, Enkripsi tingkat kolom, Enkripsi Data Transparan, Enkripsi Data Selalu, dll.
  • Halaman overflow LOB: Jika nilainya 10k, maka itu akan membutuhkan 1 halaman penuh 8k, dan kemudian bagian dari halaman ke-2. Jika tidak ada data lain yang dapat mengambil ruang yang tersisa (atau bahkan diizinkan, saya tidak yakin akan aturan itu), maka Anda memiliki kira-kira 6kb ruang "terbuang" pada datapage LOB ke-2.

  • Ruang yang tidak digunakan: Halaman data 8k hanya itu: 8192 byte. Ukurannya tidak bervariasi. Data dan meta-data yang ditempatkan di atasnya, bagaimanapun, tidak selalu cocok dengan semua 8192 byte. Dan baris tidak dapat dipisah menjadi beberapa halaman data. Jadi jika Anda memiliki 100 byte yang tersisa tetapi tidak ada baris (atau tidak ada baris yang sesuai dengan lokasi itu, tergantung pada beberapa faktor) dapat masuk ke sana, halaman data masih mengambil 8192 byte, dan permintaan 2 Anda hanya menghitung jumlah halaman data. Anda dapat menemukan nilai ini di dua tempat (perlu diingat bahwa sebagian dari nilai ini adalah sejumlah ruang yang dipesan):

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;Cari ParentObject= "PAGE HEADER:" dan Field= "m_freeCnt". The Valuelapangan adalah jumlah byte yang tidak terpakai.
    • 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;Ini adalah nilai yang sama seperti yang dilaporkan oleh "m_freeCnt". Ini lebih mudah daripada DBCC karena bisa mendapatkan banyak halaman, tetapi juga mengharuskan halaman tersebut dibaca di dalam buffer pool.
  • Ruang dicadangkan oleh FILLFACTOR<100. Halaman yang baru dibuat tidak menghormati FILLFACTORpengaturan, tetapi melakukan REBUILD akan mencadangkan ruang itu pada setiap halaman data. Gagasan di balik ruang yang dipesan adalah bahwa itu akan digunakan oleh sisipan non-sekuensial dan / atau pembaruan yang memperluas ukuran baris pada halaman, karena kolom panjang variabel diperbarui dengan sedikit lebih banyak data (tetapi tidak cukup untuk menyebabkan halaman-split). Tetapi Anda dapat dengan mudah memesan ruang pada halaman data yang secara alami tidak akan pernah mendapatkan baris baru dan tidak pernah memiliki baris yang diperbarui, atau setidaknya tidak diperbarui dengan cara yang akan meningkatkan ukuran baris.

  • Page-Splits (fragmentasi): Perlu menambahkan baris ke lokasi yang tidak memiliki ruang untuk baris akan menyebabkan pemisahan halaman. Dalam hal ini, sekitar 50% dari data yang ada dipindahkan ke halaman baru dan baris baru ditambahkan ke salah satu dari 2 halaman. Tetapi sekarang Anda memiliki sedikit lebih banyak ruang kosong yang tidak diperhitungkan dengan DATALENGTHperhitungan.

  • Baris yang ditandai untuk dihapus. Ketika Anda menghapus baris, mereka tidak selalu segera dihapus dari halaman data. Jika mereka tidak dapat segera dihapus, mereka "ditandai untuk mati" (referensi Steven Segal) dan kemudian akan dihapus secara fisik oleh proses pembersihan hantu (saya percaya itu adalah namanya). Namun, ini mungkin tidak relevan dengan Pertanyaan khusus ini.

  • Halaman hantu? Tidak yakin apakah itu istilah yang tepat, tetapi kadang-kadang halaman data tidak bisa dihapus sampai REBUILD Indeks Clustered selesai. Itu juga akan menjelaskan lebih banyak halaman daripada yang DATALENGTHakan ditambahkan. Ini umumnya tidak boleh terjadi, tetapi saya pernah mengalami satu kali, beberapa tahun yang lalu.

  • Kolom SPARSE: Kolom jarang menghemat ruang (kebanyakan untuk tipe data panjang tetap) dalam tabel di mana sebagian besar baris adalah NULLuntuk satu atau beberapa kolom. The SPARSEpilihan membuat NULLjenis nilai sampai 0 bytes (bukan normal jumlah tetap-panjang, seperti 4 byte untuk INT), namun , non-NULL nilai masing-masing mengambil sebuah tambahan 4 byte untuk jenis fixed-panjang dan jumlah variabel untuk tipe panjang variabel. Masalahnya di sini adalah bahwa DATALENGTHtidak termasuk tambahan 4 byte untuk nilai non-NULL dalam kolom SPARSE, sehingga 4 byte tersebut perlu ditambahkan kembali. Anda dapat memeriksa untuk melihat apakah ada SPARSEkolom melalui:

    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;

    Dan kemudian untuk setiap SPARSEkolom, perbarui kueri asli untuk menggunakan:

    SUM(DATALENGTH(FieldN) + 4)

    Harap perhatikan bahwa perhitungan di atas untuk menambahkan standar 4 byte agak sederhana karena hanya bekerja untuk jenis yang memiliki panjang tetap. DAN, ada meta-data tambahan per baris (dari apa yang bisa saya katakan sejauh ini) yang mengurangi ruang yang tersedia untuk data, hanya dengan memiliki setidaknya satu kolom SPARSE. Untuk detail lebih lanjut, silakan lihat halaman MSDN untuk Menggunakan Kolom Jarang .

  • Halaman indeks dan lainnya (mis. IAM, PFS, GAM, SGAM, dll): ini bukan halaman "data" dalam hal data pengguna. Ini akan mengembang ukuran total tabel. Jika menggunakan SQL Server 2012 atau yang lebih baru, Anda dapat menggunakan sys.dm_db_database_page_allocationsDynamic Management Function (DMF) untuk melihat tipe halaman (versi SQL Server sebelumnya dapat digunakan 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

    Baik DBCC INDmaupun sys.dm_db_database_page_allocations(dengan klausa WHERE) itu akan melaporkan halaman Indeks mana pun, dan hanya DBCC INDakan melaporkan setidaknya satu halaman IAM.

  • DATA_COMPRESSION: Jika Anda memiliki ROWatau PAGEKompresi diaktifkan pada Clustered Index atau Heap, maka Anda dapat melupakan sebagian besar dari apa yang telah disebutkan sejauh ini. Header Halaman 96 byte, 2 Slot Slot byte-per-baris, dan Info Versi 14 byte-per-baris masih ada, tetapi representasi fisik data menjadi sangat kompleks (lebih dari apa yang telah disebutkan ketika Kompresi sedang tidak digunakan). Misalnya, dengan Kompresi Baris, SQL Server mencoba menggunakan wadah sekecil mungkin untuk memenuhi setiap kolom, per setiap baris. Jadi jika Anda memiliki BIGINTkolom yang jika tidak (dengan asumsi SPARSEjuga tidak diaktifkan) selalu mengambil 8 byte, jika nilainya antara -128 dan 127 (yaitu ditandatangani bilangan bulat 8-bit) maka akan menggunakan hanya 1 byte, dan jika nilai bisa masuk ke dalamSMALLINT, itu hanya akan memakan waktu 2 byte. Tipe integer yang salah NULLatau tidak 0memakan ruang dan hanya ditunjukkan sebagai sedang NULLatau "kosong" (yaitu 0) dalam array memetakan kolom. Dan ada banyak, banyak aturan lainnya. Punya data Unicode ( NCHAR,, NVARCHAR(1 - 4000)tetapi tidak NVARCHAR(MAX) , bahkan jika disimpan dalam baris)? Unicode Compression ditambahkan dalam SQL Server 2008 R2, tetapi tidak ada cara untuk memprediksi hasil dari nilai "terkompresi" di semua situasi tanpa melakukan kompresi aktual mengingat kompleksitas aturan .

Jadi sungguh, permintaan kedua Anda, walaupun lebih akurat dalam hal total ruang fisik yang digunakan pada disk, hanya benar-benar akurat saat melakukan REBUILDIndeks Clustered. Dan setelah itu, Anda masih perlu memperhitungkan FILLFACTORpengaturan apa pun di bawah 100. Dan meskipun demikian selalu ada tajuk halaman, dan seringkali cukup sejumlah ruang "terbuang" yang tidak dapat diisi karena terlalu kecil untuk ditampung dalam baris apa pun di ini tabel, atau setidaknya baris yang secara logis harus masuk dalam slot itu.

Mengenai keakuratan kueri ke-2 dalam menentukan "penggunaan data", tampaknya paling adil untuk membatalkan byte Page Header karena mereka bukan penggunaan data: itu adalah biaya overhead bisnis. Jika ada 1 baris pada halaman data dan baris itu hanya a TINYINT, maka 1 byte itu tetap mensyaratkan bahwa halaman data ada dan karenanya 96 byte header. Haruskah 1 departemen dikenakan biaya untuk seluruh halaman data? Jika halaman data itu kemudian diisi oleh Departemen # 2, apakah mereka akan membagi secara merata biaya "overhead" atau membayar secara proporsional? Tampaknya paling mudah untuk mundur saja. Dalam hal ini, menggunakan nilai 8untuk menggandakan melawan number of pagesterlalu tinggi. Bagaimana tentang:

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

Karenanya, gunakan sesuatu seperti:

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

untuk semua perhitungan terhadap kolom "number_of_pages".

DAN , mengingat bahwa menggunakan DATALENGTHper bidang masing-masing tidak dapat mengembalikan meta-data per-baris, yang harus ditambahkan ke kueri per-tabel di mana Anda mendapatkan DATALENGTHper bidang masing-masing, memfilter pada setiap "departemen":

  • Jenis Rekam dan offset ke NULL Bitmap: 4 byte
  • Jumlah Kolom: 2 byte
  • Slot Array: 2 byte (tidak termasuk dalam "ukuran rekaman" tetapi masih perlu memperhitungkan)
  • NULL Bitmap: 1 byte per setiap 8 kolom (untuk semua kolom)
  • Versi Baris: 14 byte (jika database memiliki ALLOW_SNAPSHOT_ISOLATIONatau READ_COMMITTED_SNAPSHOTdiatur ke ON)
  • Kolom Panjang Variabel-Panjang Array: 0 byte jika semua kolom memiliki panjang tetap. Jika ada kolom yang panjang variabel, maka 2 byte, ditambah 2 byte per masing-masing hanya kolom panjang variabel.
  • LOB pointer: bagian ini sangat tidak tepat karena tidak akan ada pointer jika nilainya NULL, dan jika nilainya cocok pada baris maka itu bisa jauh lebih kecil atau lebih besar dari pointer, dan jika nilainya disimpan off- baris, maka ukuran pointer mungkin tergantung pada seberapa banyak data yang ada. Namun, karena kami hanya menginginkan perkiraan (mis. "Barang curian"), sepertinya 24 byte adalah nilai yang baik untuk digunakan (well, sebagus ;-) lainnya. Ini adalah per MAXbidang masing-masing .

Karenanya, gunakan sesuatu seperti:

  • Secara umum (tajuk baris + jumlah kolom + susunan slot + bitmap NULL):

    ([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
  • Secara umum (deteksi otomatis jika "info versi" ada):

    + (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())
  • JIKA ada kolom panjang variabel, lalu tambahkan:

    + 2 + (2 * {NumVariableLengthColumns})
  • JIKA ada MAX/ kolom LOB, lalu tambahkan:

    + (24 * {NumLobColumns})
  • Secara umum:

    )) AS [MetaDataBytes]

Ini tidak tepat, dan sekali lagi tidak akan berfungsi jika Anda mengaktifkan Kompresi Baris atau Halaman pada Heap atau Clustered Index, tetapi pasti akan membuat Anda lebih dekat.


PEMBARUAN Mengenai Misteri Perbedaan 15%

Kami (termasuk saya) sangat fokus pada pemikiran tentang bagaimana halaman data disusun dan bagaimana DATALENGTHmungkin menjelaskan hal-hal yang kami tidak menghabiskan banyak waktu untuk meninjau permintaan ke-2. Saya menjalankan kueri itu terhadap satu tabel dan kemudian membandingkan nilai-nilai itu dengan apa yang dilaporkan oleh sys.dm_db_database_page_allocationsdan mereka bukan nilai yang sama untuk jumlah halaman. Pada firasat, saya menghapus fungsi agregat dan GROUP BY, dan mengganti SELECTdaftar dengan a.*, '---' AS [---], p.*. Dan kemudian menjadi jelas: orang-orang harus berhati-hati di mana pada jalinan keruh ini mereka mendapatkan info dan skrip mereka dari ;-). Kueri ke-2 yang diposting di Pertanyaan tidak sepenuhnya benar, terutama untuk Pertanyaan khusus ini.

  • Masalah kecil: di luarnya tidak masuk akal untuk GROUP BY rows(dan tidak memiliki kolom dalam fungsi agregat), GABUNG antara sys.allocation_unitsdan sys.partitionssecara teknis tidak benar. Ada 3 jenis Unit Alokasi, dan salah satunya harus BERGABUNG ke bidang yang berbeda. Cukup sering partition_iddan hobt_idsama, sehingga mungkin tidak pernah ada masalah, tetapi terkadang kedua bidang tersebut memiliki nilai yang berbeda.

  • Masalah utama: kueri menggunakan used_pagesbidang. Bidang itu mencakup semua jenis halaman: Data, Indeks, IAM, dll, tc. Ada, bidang lain yang lebih tepat untuk digunakan saat yang bersangkutan dengan hanya data aktual: data_pages.

Saya menyesuaikan kueri ke-2 dalam Pertanyaan dengan item di atas, dan menggunakan ukuran halaman data yang mendukung header halaman. Saya juga menghapus dua GABUNGAN yang tidak perlu: sys.schemas(diganti dengan ajakan untuk SCHEMA_NAME()), dan sys.indexes(Indeks Clustered selalu index_id = 1dan kami ada index_iddi 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;
Solomon Rutzky
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Paul White 9
Meskipun kueri yang diperbarui yang Anda berikan untuk kueri ke-2 bahkan lebih jauh (ke arah lain sekarang :)), saya setuju dengan jawaban ini. Ini adalah kacang yang sangat sulit untuk retak tampaknya dan untuk apa nilainya, saya senang bahkan dengan para ahli membantu saya, saya masih tidak dapat menemukan alasan yang tepat mengapa kedua metode tidak cocok. Saya hanya akan menggunakan metodologi dalam jawaban lain untuk memperkirakan. Saya berharap saya bisa memilih ya untuk kedua jawaban ini, tetapi @srutzky membantu dengan semua alasan mengapa keduanya tidak aktif.
Chris Woods
6

Mungkin ini adalah jawaban grunge tetapi inilah yang akan saya lakukan.

Jadi DATALENGTH hanya menyumbang 86% dari total. Perpecahan masih sangat representatif. Overhead dalam jawaban yang sangat baik dari srutzky harus memiliki perpecahan yang cukup merata.

Saya akan menggunakan permintaan kedua Anda (halaman) untuk total. Dan gunakan yang pertama (datalength) untuk mengalokasikan split. Banyak biaya dialokasikan menggunakan normalisasi.

Dan Anda harus mempertimbangkan jawaban yang lebih dekat akan menaikkan biaya sehingga bahkan dept yang kalah dalam perpecahan masih dapat membayar lebih.

paparazzo
sumber