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:
มีพื้นที่สงวนไว้ คุณได้รับอนุญาตให้ใช้ 8060 จาก 8192 ไบต์ (นั่นคือ 132 ไบต์ที่ไม่เคยเป็นของคุณตั้งแต่แรก):
- ส่วนหัวของหน้า: นี่คือ 96 ไบต์
- Slot array: นี่คือ 2 ไบต์ต่อแถวและบ่งบอกถึงการชดเชยของที่แต่ละแถวเริ่มต้นในหน้า ขนาดของอาร์เรย์นี้ไม่ได้ จำกัด อยู่ที่ 36 ไบต์ที่เหลือ (132 - 96 = 36) มิฉะนั้นคุณจะถูก จำกัด อย่างมีประสิทธิภาพเพียงวาง 18 แถวสูงสุดในหน้าข้อมูล ซึ่งหมายความว่าแต่ละแถวมีขนาดใหญ่กว่าที่คุณคิด 2 ไบต์ ค่านี้ไม่รวมอยู่ใน "ขนาดบันทึก" ตามที่รายงานโดย
DBCC PAGE
ซึ่งเป็นสาเหตุที่แยกเก็บไว้ที่นี่แทนที่จะรวมอยู่ในข้อมูลต่อแถวด้านล่าง
- ข้อมูลเมตาต่อแถว (รวมถึง แต่ไม่ จำกัด เฉพาะ):
- ขนาดแตกต่างกันไปขึ้นอยู่กับคำจำกัดความของตาราง (เช่นจำนวนคอลัมน์ความยาวผันแปรหรือความยาวคงที่ ฯลฯ ) ข้อมูลที่นำมาจากความคิดเห็นของ @ PaulWhite และ @ Aaron ที่สามารถพบได้ในการสนทนาที่เกี่ยวข้องกับคำตอบและการทดสอบนี้
- ส่วนหัวแถว: 4 ไบต์, 2 ในนั้นแสดงถึงชนิดของเรกคอร์ด, และอีกสองอันเป็นออฟเซ็ตเป็น NULL Bitmap
- จำนวนคอลัมน์: 2 ไบต์
- โมฆะ Bitmap:
NULL
คอลัมน์ที่มีอยู่ในปัจจุบัน 1 ไบต์ต่อ 8 คอลัมน์แต่ละชุด และสำหรับคอลัมน์ทั้งหมดแม้แต่รายการNOT NULL
เดียว ดังนั้นอย่างน้อย 1 ไบต์
- อาร์เรย์ออฟเซ็ตคอลัมน์ความยาวผันแปร: ขั้นต่ำ 4 ไบต์ 2 ไบต์เพื่อเก็บจำนวนคอลัมน์ที่มีความยาวผันแปรจากนั้น 2 ไบต์ต่อคอลัมน์ที่มีความยาวผันแปรแต่ละคอลัมน์เพื่อเก็บออฟเซ็ตให้ตรงกับที่เริ่ม
- ข้อมูลการกำหนดรุ่น: 14 ไบต์ (สิ่งนี้จะปรากฏหากฐานข้อมูลของคุณถูกตั้งค่าเป็น
ALLOW_SNAPSHOT_ISOLATION ON
หรือREAD_COMMITTED_SNAPSHOT ON
)
- โปรดดูคำถามและคำตอบต่อไปนี้สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้: อาเรย์สล็อตและขนาดหน้าทั้งหมด
- โปรดดูโพสต์บล็อกต่อไปนี้จาก Paul Randall ซึ่งมีรายละเอียดที่น่าสนใจหลายประการเกี่ยวกับวิธีจัดวางหน้าข้อมูล: Poking with DBCC PAGE (ตอนที่ 1 ของ)
พอยน์เตอร์ LOB สำหรับข้อมูลที่ไม่ได้จัดเก็บไว้ในแถว นั่นจะคิดเป็นDATALENGTH
+ pointer_size แต่นี่ไม่ใช่ขนาดมาตรฐาน โปรดดูโพสต์บล็อกต่อไปนี้สำหรับรายละเอียดเกี่ยวกับหัวข้อที่ซับซ้อนนี้ขนาดของ LOB Pointer สำหรับ (MAX) ประเภทคืออะไรเช่น Varchar, Varbinary, Etc? . ระหว่างการโพสต์ที่เชื่อมโยงกับการทดสอบเพิ่มเติมที่ฉันได้ทำไว้กฎ (ค่าเริ่มต้น) ควรเป็นดังนี้:
- มรดก / เลิกลอบประเภทที่ไม่มีใครควรจะใช้อีกต่อไปเป็นของ SQL Server 2005 (
TEXT
, NTEXT
และIMAGE
):
- โดยค่าเริ่มต้นให้เก็บข้อมูลไว้ในหน้า LOB และใช้ตัวชี้ 16 ไบต์กับที่จัดเก็บ LOB เสมอ
- หากใช้ sp_tableoptionเพื่อตั้งค่า
text in row
ตัวเลือกจากนั้น:
- หากมีพื้นที่บนหน้าเพื่อเก็บค่าและค่าไม่เกินขนาดสูงสุดในแถว (ช่วงที่กำหนดได้ระหว่าง 24 - 7000 ไบต์โดยมีค่าเริ่มต้นเท่ากับ 256) จากนั้นจะถูกเก็บไว้ในแถว
- มิฉะนั้นจะเป็นตัวชี้ 16 ไบต์
- สำหรับชนิดลอบใหม่แนะนำใน SQL Server 2005 (
VARCHAR(MAX)
, NVARCHAR(MAX)
และVARBINARY(MAX)
):
- โดยค่าเริ่มต้น:
- หากค่าไม่เกิน 8000 ไบต์และมีพื้นที่ในหน้าเอกสารจะถูกเก็บไว้ในแถว
- Inline Root - สำหรับข้อมูลระหว่าง 8001 ถึง 40,000 (จริง ๆ 42,000) ไบต์อนุญาตให้มีพื้นที่จะมี 1 ถึง 5 ตัวชี้ (24 - 72 ไบต์) ในแถวที่ชี้ไปยังหน้า LOB โดยตรง 24 ไบต์สำหรับหน้า 8K LOB เริ่มต้นและ 12 ไบต์ต่อหน้า 8k เพิ่มเติมแต่ละหน้าสำหรับหน้าขนาด 8k ขึ้นอีกสี่หน้า
- TEXT_TREE - สำหรับข้อมูลที่มีมากกว่า 42,000 ไบต์หรือหากตัวชี้ 1 ถึง 5 ไม่พอดีในแถวจากนั้นจะมีเพียง 24 ไบต์ตัวชี้ไปยังหน้าเริ่มต้นของรายการตัวชี้ไปยังหน้า LOB (เช่น "text_tree" "หน้า)
- หากใช้ sp_tableoptionเพื่อตั้งค่า
large value types out of row
ตัวเลือกจากนั้นใช้ตัวชี้ 16 ไบต์เป็นที่เก็บข้อมูล LOB
- ฉันพูดว่ากฎ "default" เพราะฉันไม่ได้ทดสอบค่า in-row กับผลกระทบของคุณสมบัติบางอย่างเช่นการบีบอัดข้อมูล, การเข้ารหัสระดับคอลัมน์, การเข้ารหัสข้อมูลแบบโปร่งใส, เข้ารหัสตลอดเวลา ฯลฯ
LOB หน้าโอเวอร์โฟลว์: หากค่าคือ 10k แสดงว่าต้องมีโอเวอร์โฟลว์หน้าเต็ม 1k เต็ม 8k จากนั้นเป็นส่วนหนึ่งของหน้า 2 หากไม่มีข้อมูลอื่นสามารถใช้พื้นที่ที่เหลืออยู่ (หรือแม้กระทั่งได้รับอนุญาตฉันไม่แน่ใจในกฎนั้น) จากนั้นคุณมีพื้นที่ "สูญเปล่า" ประมาณ 6kb ของข้อมูล LOB ล้นอันที่สอง
พื้นที่ที่ไม่ได้ใช้: หน้าข้อมูล 8k เป็นเพียง: 8192 ไบต์ ขนาดไม่แตกต่างกัน อย่างไรก็ตามข้อมูลและ meta-data ที่วางอยู่บนนั้นไม่ได้พอดีกับ 8192 ไบต์ทั้งหมด และแถวไม่สามารถแบ่งออกเป็นหน้าข้อมูลหลาย ๆ หน้าได้ ดังนั้นหากคุณมี 100 ไบต์ที่เหลือ แต่ไม่มีแถว (หรือไม่มีแถวที่จะพอดีกับตำแหน่งนั้นขึ้นอยู่กับปัจจัยหลายประการ) หน้าข้อมูลจะยังคงมีอยู่ 8192 ไบต์และแบบสอบถามที่ 2 ของคุณนับเฉพาะจำนวน หน้าข้อมูล คุณสามารถหาค่านี้ได้ในสองแห่ง (โปรดจำไว้ว่าบางส่วนของค่านี้คือจำนวนของพื้นที่สงวนนั้น):
DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;
ค้นหาParentObject
= "PAGE HEADER:" และField
= "m_freeCnt" Value
ฟิลด์เป็นจำนวนไบต์ที่ไม่ได้ใช้
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;
นี่เป็นค่าเดียวกับที่รายงานโดย "m_freeCnt" สิ่งนี้ง่ายกว่า DBCC เนื่องจากสามารถรับได้หลายหน้า แต่ยังต้องการให้มีการอ่านหน้าต่างๆลงในบัฟเฟอร์พูลตั้งแต่แรก
พื้นที่ที่สงวนไว้โดยFILLFACTOR
<100 หน้าที่สร้างขึ้นใหม่ไม่เคารพการFILLFACTOR
ตั้งค่า แต่การทำ REBUILD จะเป็นการจองพื้นที่นั้นในแต่ละหน้าข้อมูล แนวคิดเบื้องหลังพื้นที่สงวนคือมันจะถูกใช้โดยส่วนแทรกแบบไม่ต่อเนื่องและ / หรือการปรับปรุงที่ขยายขนาดของแถวบนหน้าไปแล้วเนื่องจากคอลัมน์ความยาวผันแปรถูกอัปเดตด้วยข้อมูลเพิ่มเติมเล็กน้อย (แต่ไม่เพียงพอที่จะทำให้ หน้าแยก) แต่คุณสามารถจองพื้นที่บนหน้าข้อมูลที่ไม่เคยได้รับแถวใหม่ตามธรรมชาติและไม่เคยมีการปรับปรุงแถวที่มีอยู่หรืออย่างน้อยก็ไม่ได้ปรับปรุงในวิธีที่จะเพิ่มขนาดของแถว
การแบ่งหน้า (การแยกส่วน): ต้องการเพิ่มแถวไปยังตำแหน่งที่ไม่มีที่ว่างสำหรับแถวจะทำให้เกิดการแบ่งหน้า ในกรณีนี้ประมาณ 50% ของข้อมูลที่มีอยู่จะถูกย้ายไปยังหน้าใหม่และแถวใหม่จะถูกเพิ่มไปยังหนึ่งใน 2 หน้า แต่ตอนนี้คุณมีพื้นที่ว่างเพิ่มขึ้นเล็กน้อยที่ไม่ได้ถูกDATALENGTH
คำนวณ
ทำเครื่องหมายแถวเพื่อลบ เมื่อคุณลบแถวพวกเขาจะไม่ถูกลบออกทันทีจากหน้าข้อมูล หากไม่สามารถลบออกได้ในทันทีพวกเขาจะถูก "ทำเครื่องหมายเพื่อความตาย" (อ้างอิงสตีเว่นซีกัล) และจะถูกลบออกในภายหลังโดยกระบวนการล้างผี (ฉันเชื่อว่าเป็นชื่อ) อย่างไรก็ตามสิ่งเหล่านี้อาจไม่เกี่ยวข้องกับคำถามนี้
หน้าผี ไม่แน่ใจว่าเป็นคำที่เหมาะสมหรือไม่ แต่บางครั้งหน้าข้อมูลจะไม่ถูกลบจนกว่าจะสร้าง REBUILD ของ Clustered Index เสร็จแล้ว ที่จะบัญชีสำหรับหน้ามากกว่าDATALENGTH
จะเพิ่มขึ้น สิ่งนี้โดยทั่วไปไม่ควรเกิดขึ้น แต่ฉันพบเจอครั้งหนึ่งเมื่อหลายปีก่อน
คอลัมน์ SPARSE: คอลัมน์ที่กระจัดกระจายช่วยประหยัดพื้นที่ (ส่วนใหญ่สำหรับประเภทข้อมูลที่มีความยาวคงที่) ในตารางที่% ขนาดใหญ่ของแถวNULL
สำหรับคอลัมน์หนึ่งคอลัมน์หรือมากกว่า SPARSE
ตัวเลือกที่จะทำให้NULL
ประเภทค่าขึ้น 0 ไบต์ (แทนของจำนวนเงินความยาวคงปกติเช่น 4 ไบต์สำหรับINT
) แต่ไม่เป็นโมฆะค่าแต่ละใช้เวลาเพิ่มอีก 4 ไบต์สำหรับประเภทความยาวคงที่และจำนวนตัวแปร ประเภทความยาวผันแปรได้ ปัญหาที่นี่คือที่DATALENGTH
ไม่รวม 4 ไบต์พิเศษสำหรับค่าที่ไม่ใช่ NULL ในคอลัมน์ SPARSE ดังนั้นต้องเพิ่ม 4 ไบต์เหล่านั้นกลับมาคุณสามารถตรวจสอบเพื่อดูว่ามีSPARSE
คอลัมน์ใด ๆผ่าน:
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;
จากนั้นสำหรับแต่ละSPARSE
คอลัมน์ให้ปรับปรุงคิวรีดั้งเดิมที่จะใช้:
SUM(DATALENGTH(FieldN) + 4)
โปรดทราบว่าการคำนวณข้างต้นเพื่อเพิ่มในมาตรฐาน 4 ไบต์นั้นค่อนข้างง่ายเพราะใช้งานได้กับประเภทที่มีความยาวคงที่เท่านั้น และมี meta-data เพิ่มเติมต่อแถว (จากสิ่งที่ฉันสามารถบอกได้จนถึงตอนนี้) ที่ลดพื้นที่ว่างสำหรับข้อมูลเพียงแค่มีคอลัมน์ SPARSE อย่างน้อยหนึ่งคอลัมน์ สำหรับรายละเอียดเพิ่มเติมกรุณาดูที่หน้า MSDN สำหรับคอลัมน์ใช้เบาบาง
หน้าดัชนีและอื่น ๆ (เช่น IAM, PFS, GAM, SGAM และอื่น ๆ ) หน้าเหล่านี้ไม่ใช่หน้า "ข้อมูล" ในแง่ของข้อมูลผู้ใช้ สิ่งเหล่านี้จะขยายขนาดรวมของตาราง หากใช้ SQL Server 2012 หรือใหม่กว่าคุณสามารถใช้sys.dm_db_database_page_allocations
Dynamic Management Function (DMF) เพื่อดูประเภทเพจ (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
จะไม่รายงานDBCC IND
หรือหน้าsys.dm_db_database_page_allocations
(ที่มีส่วนคำสั่ง WHERE) และหน้าDBCC IND
จะรายงานอย่างน้อยหนึ่งหน้า IAM
DATA_COMPRESSION: หากคุณมีROW
หรือPAGE
เปิดใช้งานการบีบอัดในดัชนีแบบกลุ่มหรือส่วนใหญ่คุณสามารถลืมสิ่งที่ได้กล่าวถึงไปแล้วส่วนใหญ่ ส่วนหัวของหน้า 96 ไบต์, 2 อาร์เรย์ไบต์ต่อแถวและ 14 บิตต่อแถวข้อมูลการเรียงลำดับยังคงมีอยู่ แต่การแสดงทางกายภาพของข้อมูลจะซับซ้อนมาก (มากกว่าที่ได้กล่าวไว้แล้วเมื่อการบีบอัด ไม่ได้ใช้งาน) ตัวอย่างเช่นเมื่อใช้การบีบอัดแถว SQL Server จะพยายามใช้คอนเทนเนอร์ที่เล็กที่สุดเท่าที่จะเป็นไปได้เพื่อให้พอดีกับแต่ละคอลัมน์ต่อแต่ละแถว ดังนั้นหากคุณมีBIGINT
คอลัมน์ที่จะเป็นอย่างอื่น (สมมติSPARSE
ว่าไม่ได้เปิดใช้งาน) จะใช้เวลา 8 ไบต์เสมอหากค่าอยู่ระหว่าง -128 ถึง 127 (เช่นจำนวนเต็ม 8 บิตที่ลงนามแล้ว) มันจะใช้เพียง 1 ไบต์และถ้า ค่าอาจจะพอดีกับSMALLINT
มันจะใช้เวลา 2 ไบต์เท่านั้น ชนิดจำนวนเต็มที่เป็นNULL
หรือไม่0
ใช้พื้นที่และระบุเพียงว่าเป็นNULL
หรือ "ว่างเปล่า" (เช่น0
) ในการแม็พอาร์เรย์ออกคอลัมน์ และมีกฎอื่น ๆ อีกมากมาย ข้อมูลที่มี Unicode ( NCHAR
, NVARCHAR(1 - 4000)
แต่ไม่ได้ NVARCHAR(MAX)
แม้ว่าเก็บไว้ในแถว)? Unicode บีบอัดถูกเพิ่มเข้ามาใน SQL Server 2008 R2 แต่มีวิธีที่จะคาดการณ์ผลของการ "บีบอัด" ในทุกสถานการณ์โดยไม่ต้องทำการบีบอัดที่เกิดขึ้นจริงได้รับความซับซ้อนของไม่มีกฎ
ดังนั้นคำถามที่สองของคุณในขณะที่แม่นยำยิ่งขึ้นในแง่ของพื้นที่ทางกายภาพทั้งหมดที่ใช้บนดิสก์นั้นจะมีความถูกต้องจริง ๆ เท่านั้นเมื่อทำREBUILD
ดัชนี Clustered และหลังจากนั้นคุณยังต้องคำนึงถึงการFILLFACTOR
ตั้งค่าที่ต่ำกว่า 100 และถึงแม้จะมีส่วนหัวของหน้าอยู่เสมอและมักจะมีเนื้อที่ "สูญเปล่า" ที่เพียงพอซึ่งไม่สามารถเติมได้เนื่องจากมีขนาดเล็กเกินไปที่จะพอดีกับแถวในแถวนี้ ตารางหรืออย่างน้อยแถวที่มีเหตุผลควรเข้าไปในช่องนั้น
เกี่ยวกับความถูกต้องของการสืบค้นครั้งที่ 2 ในการพิจารณา "การใช้ข้อมูล" ดูเหมือนว่ายุติธรรมที่สุดในการสำรองข้อมูลส่วนหัวของเพจเนื่องจากไม่ใช่การใช้ข้อมูล: เป็นค่าใช้จ่ายทางธุรกิจ หากมี 1 แถวในหน้าข้อมูลและแถวนั้นมีเพียง a TINYINT
ดังนั้น 1 ไบต์นั้นยังจำเป็นต้องมีหน้าข้อมูลอยู่และด้วยเหตุนี้จึงมี 96 ไบต์ของส่วนหัว ควรคิดค่าบริการ 1 แผนกสำหรับหน้าข้อมูลทั้งหมดหรือไม่ หากหน้าข้อมูลนั้นเต็มไปด้วยแผนก # 2 พวกเขาจะแบ่งค่าใช้จ่าย "ค่าใช้จ่าย" หรือจ่ายตามสัดส่วนอย่างเท่าเทียมกันหรือไม่ ดูเหมือนจะง่ายที่สุดในการสำรอง ในกรณีนี้การใช้ค่า8
ทวีคูณเทียบกับnumber of pages
นั้นสูงเกินไป เกี่ยวกับ:
-- 8192 byte data page - 96 byte header = 8096 (approx) usable bytes.
SELECT 8060.0 / 1024 -- 7.906250
ดังนั้นให้ใช้สิ่งที่ชอบ:
(SUM(a.total_pages) * 7.91) / 1024 AS [TotalSpaceMB]
สำหรับการคำนวณทั้งหมดกับคอลัมน์ "number_of_pages"
และเนื่องจากการใช้DATALENGTH
ต่อแต่ละฟิลด์ไม่สามารถส่งคืนเมตาดาต้าต่อแถวซึ่งควรเพิ่มลงในคิวรีต่อตารางของคุณซึ่งคุณจะได้รับDATALENGTH
ข้อมูลแต่ละฟิลด์โดยกรองแต่ละแผนก ":
- ประเภทเร็กคอร์ดและออฟเซ็ตเป็น NULL Bitmap: 4 ไบต์
- จำนวนคอลัมน์: 2 ไบต์
- Array ของช่อง: 2 ไบต์ (ไม่รวมอยู่ใน "ขนาดบันทึก" แต่ยังต้องมีการบัญชี)
- NULL Bitmap: 1 ไบต์ต่อทุกๆ 8 คอลัมน์ (สำหรับคอลัมน์ทั้งหมด )
- การกำหนดเวอร์ชันของแถว: 14 ไบต์ (หากฐานข้อมูลมี
ALLOW_SNAPSHOT_ISOLATION
หรือREAD_COMMITTED_SNAPSHOT
ตั้งค่าเป็นON
)
- คอลัมน์ความยาวแปรผันอาร์เรย์ออฟเซ็ต: 0 ไบต์หากคอลัมน์ทั้งหมดมีความยาวคงที่ หากคอลัมน์ใดมีความยาวผันแปรดังนั้น 2 ไบต์และบวก 2 ไบต์ต่อคอลัมน์ความยาวแปรผันเท่านั้น
- พอยน์เตอร์ LOB: ส่วนนี้ไม่แน่ชัดเนื่องจากไม่มีตัวชี้หากค่าเป็น
NULL
และถ้าค่าพอดีกับแถวมันจะเล็กกว่าหรือใหญ่กว่าตัวชี้มากและถ้าเก็บค่าไว้ แถวจากนั้นขนาดของตัวชี้อาจขึ้นอยู่กับจำนวนข้อมูลที่มี อย่างไรก็ตามเนื่องจากเราต้องการประมาณการ (เช่น "swag") ดูเหมือนว่า 24 ไบต์เป็นค่าที่ดีในการใช้ (เช่นเดียวกับอื่น ๆ ;-) นี่คือแต่ละMAX
ฟิลด์
ดังนั้นให้ใช้สิ่งที่ชอบ:
โดยทั่วไป (ส่วนหัวแถว + จำนวนคอลัมน์ + อาร์เรย์สล็อต + บิตแมป NULL):
([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
โดยทั่วไป (ตรวจสอบอัตโนมัติหากมี "ข้อมูลรุ่น" อยู่):
+ (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())
หากมีคอลัมน์ที่มีความยาวผันแปรให้เพิ่ม:
+ 2 + (2 * {NumVariableLengthColumns})
หากมีMAX
คอลัมน์ / LOB ใด ๆให้เพิ่ม:
+ (24 * {NumLobColumns})
โดยทั่วไป:
)) AS [MetaDataBytes]
สิ่งนี้ไม่ถูกต้องและอีกครั้งจะไม่ทำงานหากคุณเปิดใช้งานแถวหรือการบีบอัดหน้าในดัชนีกองหรือกลุ่ม แต่ควรให้คุณเข้าใกล้อย่างแน่นอน
อัปเดตเกี่ยวกับความแตกต่างลึกลับ 15%
เรา (รวมถึงตัวเอง) มุ่งเน้นที่การคิดเกี่ยวกับวิธีการจัดวางหน้าข้อมูลและวิธีการที่DATALENGTH
บัญชีสำหรับสิ่งที่เราไม่ได้ใช้เวลามากในการตรวจสอบแบบสอบถามที่ 2 ฉันเรียกใช้คิวรีนั้นกับตารางเดียวแล้วเปรียบเทียบค่าเหล่านั้นกับสิ่งที่ถูกรายงานโดยsys.dm_db_database_page_allocations
พวกเขาไม่ใช่ค่าเดียวกันสำหรับจำนวนหน้า เกี่ยวกับลางสังหรณ์ผมเอาฟังก์ชั่นรวมและGROUP BY
และแทนที่รายการที่มีSELECT
a.*, '---' AS [---], p.*
และจากนั้นมันก็ชัดเจน: ผู้คนจะต้องระมัดระวังในที่ที่มืดมัวเข้าด้วยกันพวกเขาได้รับข้อมูลและสคริปต์จาก ;-) แบบสอบถามที่ 2 ที่โพสต์ในคำถามนั้นไม่ถูกต้องโดยเฉพาะอย่างยิ่งสำหรับคำถามนี้โดยเฉพาะ
ปัญหาเล็ก ๆ น้อย ๆ : นอกจากจะไม่ค่อยสมเหตุสมผลGROUP BY rows
(และไม่มีคอลัมน์นั้นในฟังก์ชั่นรวม) การเข้าร่วมระหว่างsys.allocation_units
และsys.partitions
ไม่ถูกต้องทางเทคนิค หน่วยการจัดสรรมี 3 ประเภทและหนึ่งในนั้นควรรวมเข้ากับฟิลด์อื่น บ่อยครั้งpartition_id
และhobt_id
เหมือนกันดังนั้นอาจไม่มีปัญหา แต่บางครั้งทั้งสองฟิลด์มีค่าต่างกัน
ปัญหาที่สำคัญ: แบบสอบถามใช้used_pages
เขตข้อมูล ฟิลด์นั้นครอบคลุมหน้าเว็บทุกประเภท: ข้อมูล, ดัชนี, IAM, ฯลฯ , tc data_pages
มีอีกข้อมูลที่เหมาะสมต่อการใช้งานเมื่อเกี่ยวข้องกับเฉพาะข้อมูลที่เกิดขึ้นจริงคือ
ฉันปรับคำถามที่ 2 ในคำถามโดยคำนึงถึงรายการข้างต้นและใช้ขนาดหน้าข้อมูลที่สำรองส่วนหัวของหน้า ฉันยังเอาออกสองร่วมที่ไม่จำเป็น: sys.schemas
(แทนที่ด้วยการเรียกร้องให้SCHEMA_NAME()
) และsys.indexes
(ดัชนีคลัสเตอร์อยู่เสมอindex_id = 1
และเรามีindex_id
ใน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;