Sys.allocation_units öğesinden tablo boyutuyla eşleşmeyen DATALENGTH toplamı


11

DATALENGTH()Bir tablodaki tüm kayıtlar için tüm alanları toplayacak olursam, tablonun toplam boyutunu alacağım izlenimi altındaydım . Yanlış mıyım?

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

Aşağıdaki veritabanımı belirli bir tablonun boyutunu almak için (ben tablo boyutları, kümelenmiş dizinleri sadece bu nedenle NC dizinleri içermez) almak için bu sorguyu kullandım. Faturalama amaçları için (departmanlarımızı kullandıkları alan miktarına göre ücretlendiriyoruz) Her bir departmanın bu tabloda ne kadar alan kullandığını bulmam gerekiyor. Tablodaki her grubu tanımlayan bir sorgu var. Sadece her grubun ne kadar yer kapladığını bulmam gerekiyor.

VARCHAR(MAX)Tablodaki alanlar nedeniyle satır başına boşluk çılgınca sallanabilir , bu yüzden bir bölüm için satırların ortalama boyutunu * alamam. I kullandığınızda DATALENGTH()I, yukarıda tarif edilen bir yaklaşım, sadece, aşağıdaki terimi kullanılan toplam alanının% 85'ini almak. Düşünceler?

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

Her bölüm için filtrelenmiş bir dizin oluşturmam veya tabloyu bölümlemem önerildi, böylece dizin başına kullanılan alanı doğrudan sorgulayabiliyorum. Filtrelenmiş dizinler, her zaman alanı kullanmak yerine programlı olarak oluşturulabilir (ve bir bakım penceresi sırasında veya periyodik faturalandırmayı gerçekleştirmem gerektiğinde tekrar bırakılabilir) (bölümler bu açıdan daha iyi olurdu).

Bu öneriyi seviyorum ve genellikle bunu yapardım. Ama dürüst olmak gerekirse, "her bölümü" neden buna ihtiyacım olduğunu açıklamak için bir örnek olarak kullanıyorum, ama dürüst olmak gerekirse, bu gerçekten neden değil. Gizlilik nedeniyle, bu verilere neden ihtiyacımın kesin nedenini açıklayamam, ancak farklı departmanlara benzer.

Bu tablodaki kümelenmemiş dizinlerle ilgili olarak: NC dizinlerinin boyutlarını alabilirsem, bu harika olurdu. Ancak, NC dizinleri kümelenmiş dizinin boyutunun <% 1'inden sorumludur, bu nedenle bunları dahil etmiyoruz. Ancak yine de NC dizinlerini nasıl ekleriz? Kümelenmiş dizin için doğru bir boyut bile alamıyorum :)


Özünde, iki sorunuz var: (1) satır uzunluklarının toplamı neden meta verilerin tüm tablonun büyüklüğüne göre muhasebeleştirilmiyor? Aşağıdaki cevap, en azından kısmen (ve sürüm başına ve özellik başına, örneğin sıkıştırma, sütun deposu, vb.) Dalgalanma gösterebilir. Ve daha da önemlisi: (2) bölüm başına kullanılan gerçek alanı nasıl doğru bir şekilde belirleyebilirsiniz? Bunu doğru bir şekilde yapabileceğinizi bilmiyorum - çünkü cevapta yer alan bazı veriler için hangi departmana ait olduğunu söylemenin bir yolu yok.
Aaron Bertrand

Sorunun, kümelenmiş dizin için doğru bir boyuta sahip olmamanız olduğunu düşünmüyorum - meta veriler kesinlikle dizininizin ne kadar yer kapladığını size kesin olarak söyler. Meta verilerin size - en azından mevcut tasarımınıza / yapınıza göre - her departmanla ne kadar verinin ilişkilendirildiğini söylemek için tasarlanmamıştır.
Aaron Bertrand

Yanıtlar:


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.

8k veri sayfasında yer alan tek şey veri değildir:

  • Ayrılmış alan var. Yalnızca 8192 baytın 8060'ını kullanmanıza izin verilir (bu, ilk etapta asla sizin olmayan 132 bayttır):

    • Sayfa başlığı: Bu tam olarak 96 bayttır.
    • Yuva dizisi: bu satır başına 2 bayttır ve her satırın sayfada başladığı uzaklığı gösterir. Bu dizinin boyutu kalan 36 baytla (132 - 96 = 36) sınırlı değildir, aksi takdirde bir veri sayfasına maksimum 18 satır koymakla etkin bir şekilde sınırlı kalırsınız. Bu, her satırın düşündüğünüzden 2 bayt daha büyük olduğu anlamına gelir. Bu değer, rapor edildiği gibi "kayıt boyutu" na dahil değildir DBCC PAGE, bu nedenle aşağıdaki satır başına bilgilere dahil olmak yerine burada ayrı tutulur.
    • Satır Başına meta veriler (bunlarla sınırlı olmamak üzere):
      • Boyut, tablo tanımına (yani sütun sayısı, değişken uzunluk veya sabit uzunluk vb.) Bağlı olarak değişir. Bu cevap ve testle ilgili tartışmada bulunan @ PaulWhite's ve @ Aaron'un yorumlarından alınan bilgiler .
      • Satır başlığı: 2'si kayıt türünü gösteren 4 bayt ve diğer ikisi NULL Bitmap'e bir mahsup
      • Sütun sayısı: 2 bayt
      • NULL Bitmap: şu anda hangi sütunlar NULL. 8 sütunluk her set için 1 bayt. Ve tüm sütunlar, hatta sütunlar için NOT NULL. Bu nedenle, en az 1 bayt.
      • Değişken uzunlukta sütun ofseti dizisi: minimum 4 bayt. Değişken uzunluktaki sütunların sayısını tutmak için 2 bayt ve sonra da ofsetin başladığı yeri tutmak için her değişken uzunluktaki sütun başına 2 bayt.
      • Sürüm Bilgisi: 14 bayt (eğer veritabanınız ya ALLOW_SNAPSHOT_ISOLATION ONda olarak ayarlanmışsa mevcut olacaktır READ_COMMITTED_SNAPSHOT ON).
    • Bununla ilgili daha fazla bilgi için lütfen aşağıdaki Soru ve Cevaplara bakın: Yuva Dizisi ve Toplam Sayfa Boyutu
    • Lütfen veri sayfalarının nasıl düzenlendiğiyle ilgili birkaç ilginç ayrıntıya sahip olan Paul Randall'ın aşağıdaki blog yayınına bakın: DBCC PAGE ile uğraşmak (Bölüm 1?)
  • Satırda saklanmayan veriler için LOB işaretçileri. Böylece DATALENGTH+ pointer_size açıklanır. Ancak bunlar standart büyüklükte değildir. Bu karmaşık konuyla ilgili ayrıntılar için lütfen aşağıdaki blog gönderisine bakın: Varchar, Varbinary, Etc gibi (MAX) Tipler için LOB İşaretçisinin Boyutu Nedir? . Bağlantılı yazı ile yaptığım bazı ek testler arasında , (varsayılan) kurallar aşağıdaki gibi olmalıdır:

    • Eski / kimse (SQL Server 2005 itibarıyla artık kullanarak gerektiğini LOB türlerini kullanımdan kaldırıldı TEXT, NTEXTve IMAGE):
      • Varsayılan olarak, verilerini her zaman LOB sayfalarında saklayın ve LOB depolamaya her zaman 16 baytlık bir işaretçi kullanın.
      • EĞER sp_tableoption ayarlamak için kullanıldı text in row, sonra seçeneği:
        • sayfada değeri saklamak için alan varsa ve değer satır içi maksimum boyuttan büyük değilse (varsayılan 256 ile 7 - 7000 bayt arasında yapılandırılabilir aralık), satırda depolanır,
        • aksi takdirde 16 baytlık bir işaretçi olacaktır.
    • SQL Server 2005'te tanıtılan yeni LOB türleri için ( VARCHAR(MAX), NVARCHAR(MAX)ve VARBINARY(MAX)):
      • Varsayılan olarak:
        • Değeri büyüktür 8000 byte değil, varsa ve sayfadaki oda var, o zaman içinde satır saklanacaktır.
        • Satır İçi Kök - 8001 ve 40.000 (gerçekten 42.000) bayt arasındaki veriler için, alan izin verirse, doğrudan LOB sayfalarına yönlendiren 1 ila 5 işaretçi (24 - 72 bayt) olacaktır. İlk 8k LOB sayfası için 24 bayt ve dört adede kadar 8k sayfa için her ek 8k sayfa başına 12 bayt.
        • TEXT_TREE - 42.000 baytın üzerindeki veriler için veya 1 ila 5 işaretçi sıraya sığmazsa, LOB sayfalarına işaretçiler listesinin başlangıç ​​sayfasında yalnızca 24 baytlık bir işaretçi olur (ör. "Text_tree" "sayfası).
      • EĞER sp_tableoption ayarlamak için kullanılan large value types out of rowseçenek, daha sonra her zaman LOB depolama için 16 baytlık işaretçisi kullanabilirsiniz.
    • Ben yaptım "varsayılan" kurallar nedeniyle söz konusu değildir vb Veri Sıkıştırma, Sütun düzey Şifreleme, Şeffaf Veri Şifreleme, daima Şifreli gibi belirli özelliklerin etkisine karşı içinde satır değerleri test
  • LOB taşma sayfaları: Bir değer 10k ise, bu 1 tam 8k sayfa taşma ve daha sonra 2. sayfanın bir bölümünü gerektirir. Kalan alanı kaplayabilecek başka bir veri yoksa (veya bu kurala bile izin verilmiyorsa), bu 2. LOB taşma veri sayfasında yaklaşık 6 kb'lik "boşa" alanınız vardır.

  • Kullanılmayan alan: 8k veri sayfası şudur: 8192 bayt. Boyut olarak değişmez. Bununla birlikte, üzerine yerleştirilen veriler ve meta veriler her zaman 8192 baytın hepsine tam olarak uymaz. Ve satırlar birden çok veri sayfasına bölünemez. Dolayısıyla, 100 baytınız kaldı ancak satırınız (veya birkaç faktöre bağlı olarak o konuma sığacak hiçbir satır) oraya sığamazsa, veri sayfası hala 8192 bayt kaplıyor ve 2. sorgunuz yalnızca veri sayfaları. Bu değeri iki yerde bulabilirsiniz (bu değerin bir kısmının ayrılmış alanın bir kısmı olduğunu unutmayın):

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;İçin bak ParentObject= "Sayfa başlığı:" ve Field= "m_freeCnt". ValueAlan kullanılmayan bayt sayısıdır.
    • 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;Bu, "m_freeCnt" tarafından bildirilen değerle aynıdır. Bu, birçok sayfa alabildiğinden DBCC'den daha kolaydır, ancak sayfaların ilk olarak arabellek havuzuna okunmasını gerektirir.
  • FILLFACTOR<100 tarafından ayrılan alan. Yeni oluşturulan sayfalar FILLFACTORayara uymaz, ancak REBUILD yapmak her veri sayfasında bu alanı ayıracaktır. Ayrılmış alanın arkasındaki fikir, değişken uzunluktaki sütunların biraz daha fazla veriyle güncellenmesi nedeniyle (ancak bir sayfa bölünmüş). Ancak, doğal olarak hiçbir zaman yeni satır almayan ve mevcut satırları hiçbir zaman güncellemeyen veya en azından satırın boyutunu artıracak şekilde güncellenmeyen veri sayfalarında yer ayırabilirsiniz.

  • Sayfa Bölmeleri (parçalanma): Satır için yer bulunmayan bir konuma satır eklemeniz gerektiğinde sayfa bölünmesine neden olur. Bu durumda, mevcut verilerin yaklaşık% 50'si yeni bir sayfaya taşınır ve yeni satır 2 sayfadan birine eklenir. Ama şimdi DATALENGTHhesaplamalarla açıklanmayan biraz daha fazla boş alanınız var .

  • Silinmek üzere işaretlenmiş satırlar. Satırları sildiğinizde, satırlar her zaman veri sayfasından hemen kaldırılmaz. Hemen kaldırılamazlarsa, "ölüm için işaretlenir" (Steven Segal referansı) ve daha sonra hayalet temizleme işlemi ile fiziksel olarak kaldırılacaktır (bunun adı olduğuna inanıyorum). Ancak, bunlar bu Özel Soru ile ilgili olmayabilir.

  • Hayalet sayfalar? Bunun doğru terim olup olmadığından emin değilim, ancak bazen Veri Sayfaları Kümelenmiş Dizinin YENİDEN YAPILMASI yapılıncaya kadar kaldırılmaz. Bu, DATALENGTHekleyebileceğinden daha fazla sayfayı da hesaba katar. Bu genellikle olmamalı, ama birkaç yıl önce bir kez karşılaştım.

  • SPARSE sütunları: Seyrek sütunlar, satırların büyük bir yüzdesinin NULLbir veya daha fazla sütun için olduğu tablolarda yerden tasarruf sağlar (çoğunlukla sabit uzunluktaki veri türleri için) . Bu SPARSEseçenek, NULLdeğer türünü 0 bayt yapar (an için 4 bayt gibi normal sabit uzunluk miktarı yerine INT), ancak NULL olmayan değerlerin her biri sabit uzunluklu türler için ek 4 bayt ve değişken uzunluklu tipler. Buradaki sorun DATALENGTH, bir SPARSE sütununda NULL olmayan değerler için fazladan 4 bayt içermemesidir, bu nedenle bu 4 baytın tekrar eklenmesi gerekir. SPARSEYoluyla herhangi bir sütun olup olmadığını kontrol edebilirsiniz :

    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;

    Ve sonra her SPARSEsütun için kullanılacak orijinal sorguyu güncelleyin:

    SUM(DATALENGTH(FieldN) + 4)

    Standart 4 bayt eklemek için yukarıdaki hesaplamanın sadece basit uzunluklu türlerde çalıştığı için biraz basit olduğunu lütfen unutmayın. VE, satır başına ek veri (şimdiye kadar söyleyebileceğim kadarıyla) vardır, bu da sadece en az bir SPARSE sütununa sahip veriler için kullanılabilir alanı azaltır. Daha fazla ayrıntı için, lütfen Seyrek Sütunları Kullanma için MSDN sayfasına bakın .

  • Dizin ve diğer (örneğin IAM, PFS, GAM, SGAM vb.) Sayfalar: bunlar kullanıcı verileri açısından "veri" sayfaları değildir. Bunlar tablonun toplam boyutunu şişirir. SQL Server 2012 veya daha sys.dm_db_database_page_allocationsyenisini kullanıyorsanız , sayfa türlerini görmek için Dinamik Yönetim İşlevi'ni (DMF) kullanabilirsiniz (SQL Server'ın önceki sürümleri kullanabilir 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

    Ne DBCC INDde sys.dm_db_database_page_allocations(bu WHERE yan tümcesiyle birlikte) herhangi bir Dizin sayfasını bildirmez ve yalnızca DBCC INDen az bir IAM sayfası bildirir.

  • DATA_COMPRESSION: Kümelenmiş Dizin veya Öbek üzerinde Etkinleştirdiğiniz ROWveya PAGESıkıştırma özelliğini etkinleştirdiyseniz, şu ana kadar bahsedilenlerin çoğunu unutabilirsiniz. 96 bayt Sayfa Başlığı, satır başına 2 bayt Yuvası Dizisi ve satır başına 14 bayt Sürüm Bilgisi hala mevcuttur, ancak verilerin fiziksel gösterimi oldukça karmaşık hale gelir (Sıkıştırma sırasında daha önce bahsedilenlerden çok daha fazla) kullanılmıyor). Örneğin, Satır Sıkıştırma ile SQL Server, her satır başına, her sütuna sığdırmak için mümkün olan en küçük kapsayıcı kullanmaya çalışır. Bu nedenle BIGINT, aksi halde ( SPARSEetkin olmadığı varsayılırsa ) her zaman 8 bayt alırsa, değer -128 ile 127 arasındaysa (yani işaretli 8 bit tam sayı) varsa, yalnızca 1 bayt kullanır ve değer birSMALLINTyalnızca 2 bayt alır. Boşluk alan NULLveya 0yer kaplamayan ve sütunları eşleyen bir dizide NULL"boş" (yani 0) olarak gösterilen tamsayı türleri . Ve daha birçok kural var. Var Unicode verileri ( NCHAR, NVARCHAR(1 - 4000)fakat değil NVARCHAR(MAX) , içinde satır saklanan bile)? Unicode Sıkıştırma, SQL Server 2008 R2'ye eklendi, ancak kuralların karmaşıklığı göz önüne alındığında, gerçek sıkıştırmayı yapmadan tüm durumlarda "sıkıştırılmış" değerin sonucunu tahmin etmenin bir yolu yoktur .

Aslında, ikinci sorgunuz, diskte toplanan toplam fiziksel alan açısından daha doğru olsa REBUILDda, Kümelenmiş Dizin'den biri yapıldığında gerçekten doğrudur . Ve bundan sonra, yine de FILLFACTOR100'ün altındaki herhangi bir ayarı hesaba katmanız gerekir. Ve o zaman bile her zaman sayfa başlıkları vardır ve genellikle bu satırdaki herhangi bir satıra sığmayacak kadar küçük olduğu için doldurulamayan yeterli miktarda "boşa" alan vardır. tablo veya en azından mantıksal olarak o yuvaya girmesi gereken satır.

"Veri kullanımını" belirleme konusunda 2. sorgunun doğruluğuyla ilgili olarak, veri kullanımı olmadığı için Sayfa Üstbilgisi baytlarını geri almak en adil görünmektedir: bunlar işletme maliyeti yüküdür. Bir veri sayfasında 1 satır varsa ve bu satır yalnızca a ise TINYINT, o 1 bayt hala veri sayfasının var olmasını ve dolayısıyla başlığın 96 baytını gerektirir. Bu 1 departman tüm veri sayfası için ücretlendirilmeli mi? Bu veri sayfası daha sonra Bölüm # 2 tarafından doldurulursa, bu "genel gider" maliyetini eşit olarak bölerler mi yoksa orantılı olarak öderler mi? Sadece geri almak en kolay görünüyor. Bu durumda, 8çarpma değeri kullanmak number of pagesçok yüksektir. Nasıl olur:

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

Bu nedenle, şöyle bir şey kullanın:

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

"number_of_pages" sütunlarına karşı tüm hesaplamalar için.

AND , DATALENGTHher bir alan için kullanmanın satır başına meta verilerini döndüremeyeceğini düşünürsek, her bir alan için DATALENGTHfiltreleme yaparak her bir alan için filtreleme yaparak tablo başına sorgunuza eklenmelidir :

  • Kayıt Türü ve ofset NULL Bitmap: 4 bayt
  • Sütun Sayısı: 2 bayt
  • Yuva Dizisi: 2 bayt ("kayıt boyutuna" dahil değildir, ancak yine de hesaba katılması gerekir)
  • NULL Bitmap: 8 sütun başına 1 bayt ( tüm sütunlar için)
  • Satır Sürüm Oluşturma: 14 bayt (veritabanından biri varsa ALLOW_SNAPSHOT_ISOLATIONveya olarak READ_COMMITTED_SNAPSHOTayarlanırsa ON)
  • Değişken uzunlukta sütun Ofset Dizisi: Tüm sütunlar sabit uzunlukta ise 0 bayt. Herhangi bir sütun değişken uzunluktaysa, o zaman 2 bayt artı yalnızca değişken uzunluktaki sütunların her biri için 2 bayt.
  • LOB işaretçileri: değer bir işaretçi olmayacağı için bu bölüm çok kesin değildir NULLve değer satıra sığarsa, işaretçiden çok daha küçük veya çok daha büyük olabilir ve değer saklanırsa sonra, işaretçinin boyutu, ne kadar veri olduğuna bağlı olabilir. Ancak, sadece bir tahmin istediğimizden (yani "yağma"), 24 baytın kullanılması iyi bir değer gibi görünüyor (iyi, diğer herhangi bir şey gibi ;-). Bu, her MAXalan için ayrıdır.

Bu nedenle, şöyle bir şey kullanın:

  • Genel olarak (satır başlığı + sütun sayısı + alan dizisi + NULL bitmap):

    ([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
  • Genel olarak ("sürüm bilgisi" varsa otomatik algılama):

    + (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())
  • EĞER değişken uzunluklu sütunlar varsa, ekleyin:

    + 2 + (2 * {NumVariableLengthColumns})
  • Herhangi vardır EĞER MAX/ LOB sütunları, daha sonra ekleyin:

    + (24 * {NumLobColumns})
  • Genel olarak:

    )) AS [MetaDataBytes]

Bu kesin değildir ve Yığın veya Kümelenmiş Dizin'de Satır veya Sayfa Sıkıştırma'yı etkinleştirdiyseniz yine işe yaramaz, ancak kesinlikle sizi daha da yaklaştırmanız gerekir.


% 15 Fark Gizemine İlişkin GÜNCELLEME

Biz (kendim dahil) veri sayfalarının nasıl düzenlendiğini ve DATALENGTH2. sorguyu gözden geçirmek için çok fazla zaman harcamadığımız şeyleri nasıl açıklayabileceğimizi düşünmeye odaklandık . Bu sorguyu tek bir tabloya karşı çalıştırdım ve daha sonra bu değerleri raporlayanlarla karşılaştırdım sys.dm_db_database_page_allocationsve sayfa sayısı için aynı değerler değildi. Bir yığınta, toplama işlevlerini kaldırdım GROUP BYve SELECTlisteyi değiştirdim a.*, '---' AS [---], p.*. Ve sonra netleşti: insanlar bu karanlık iç içe web'lerde ;-) bilgi ve senaryolarını nereden aldıklarına dikkat etmelidir. Soruda yayınlanan 2. sorgu, özellikle bu soru için tam olarak doğru değildir.

  • Küçük sorun: bunun dışında çok fazla mantıklı GROUP BY rowsdeğil (ve bu sütunu bir toplu işleve sahip değil), aradaki JOIN sys.allocation_unitsve sys.partitionsteknik olarak doğru değil. 3 tip Tahsis Birimi vardır ve bunlardan biri farklı bir alana katılmalıdır. Oldukça sık partition_idve hobt_idaynıdır, bu yüzden asla bir sorun olmayabilir, ancak bazen bu iki alanın farklı değerleri vardır.

  • Büyük sorun: sorgu used_pagesalanı kullanır . Bu alan tüm sayfa türlerini kapsar : Veri, Dizin, IAM, vb. Tc. Sadece gerçek verilerle ilgili zaman başka, daha uygun alan kullanımına vardır: data_pages.

Sorudaki 2. sorguyu yukarıdaki öğeleri göz önünde bulundurarak ve sayfa başlığını yedekleyen veri sayfası boyutunu kullanarak uyarladım. : Ayrıca iki o gereksiz JOIN kaldırıldı sys.schemas(çağrısına ile değiştirilir SCHEMA_NAME()) ve sys.indexes(kümelenmiş dizin her zaman index_id = 1ve sahip olduğumuz index_idiçinde 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;

Yorumlar uzun tartışmalar için değildir; bu görüşme sohbete taşındı .
Paul White 9

2. sorgu için sağladığınız güncelleştirilmiş sorgu daha da kapalı olmasına rağmen (şimdi diğer yönde :)), ben bu cevap ile iyiyim. Bu görünüşte çatlamak için çok zor bir somun ve değer ne için, bana yardımcı olan uzmanlardan bile memnunum, iki yöntemin eşleşmemesinin kesin nedenini hala anlayamadım. Ben sadece diğer cevaptaki metodolojiyi kullanacağım. Keşke bu cevapların her ikisi için de evet oy verebilseydim, ancak srutzky bu ikisinin kapalı olmasının tüm nedenlerine yardım etti.
Chris Woods

6

Belki bu bir grunge cevabı ama bunu yapardım.

DATALENGTH toplamın sadece% 86'sını oluşturur. Hala çok temsili bir bölünme. Srutzky'nin mükemmel cevabındaki yük, oldukça eşit bir bölünmeye sahip olmalıdır.

Toplam için ikinci sorgunuzu (sayfalar) kullanırdım. Ve bölünmeyi ayırmak için ilki (veri uzunluğu) kullanın. Birçok maliyet normalleştirme kullanılarak tahsis edilir.

Ve daha yakın bir cevabın maliyeti artıracağını düşünmelisiniz, böylece bir bölünmede kaybedilen borç bile daha fazla ödeme yapabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.