SUM của DATALENGTH không khớp với kích thước bảng từ sys.allocation_units


11

Tôi có ấn tượng rằng nếu tôi tổng hợp DATALENGTH()tất cả các trường cho tất cả các bản ghi trong một bảng thì tôi sẽ có được tổng kích thước của bảng. Tôi có nhầm không?

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

Tôi đã sử dụng truy vấn này bên dưới (mà tôi nhận được từ trực tuyến để lấy kích thước bảng, chỉ các cụm chỉ mục để nó không bao gồm các chỉ mục NC) để lấy kích thước của một bảng cụ thể trong cơ sở dữ liệu của tôi. Đối với mục đích thanh toán (chúng tôi tính phí các phòng ban của chúng tôi theo số lượng không gian họ sử dụng) Tôi cần tính xem mỗi phòng ban được sử dụng bao nhiêu không gian trong bảng này. Tôi có một truy vấn xác định từng nhóm trong bảng. Tôi chỉ cần tìm ra mỗi nhóm chiếm bao nhiêu không gian.

Không gian trên mỗi hàng có thể dao động dữ dội do VARCHAR(MAX)các trường trong bảng, vì vậy tôi không thể lấy kích thước trung bình * tỷ lệ của các hàng cho một bộ phận. Khi tôi sử dụng DATALENGTH()cách tiếp cận được mô tả ở trên, tôi chỉ nhận được 85% tổng dung lượng được sử dụng trong truy vấn bên dưới. Suy nghĩ?

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

Có ý kiến ​​cho rằng tôi tạo một chỉ mục được lọc cho từng bộ phận hoặc phân vùng bảng, vì vậy tôi có thể truy vấn trực tiếp không gian được sử dụng cho mỗi chỉ mục. Các chỉ mục được lọc có thể được tạo lập trình (và được loại bỏ một lần nữa trong cửa sổ bảo trì hoặc khi tôi cần thực hiện thanh toán định kỳ), thay vì sử dụng không gian mọi lúc (phân vùng sẽ tốt hơn về mặt này).

Tôi thích đề xuất đó và thường sẽ làm điều đó. Nhưng thành thật mà nói, tôi sử dụng "mỗi phòng" làm ví dụ để giải thích lý do tại sao tôi cần điều này, nhưng thành thật mà nói, đó không thực sự là lý do. Vì lý do bảo mật, tôi không thể giải thích lý do chính xác tại sao tôi cần dữ liệu này, nhưng nó tương tự với các bộ phận khác nhau.

Về các chỉ mục không bao gồm trong bảng này: Nếu tôi có thể nhận được kích thước của các chỉ mục NC, điều đó sẽ rất tuyệt. Tuy nhiên, các chỉ mục NC chiếm <1% kích thước của chỉ mục được nhóm, vì vậy chúng tôi không bao gồm những chỉ số đó. Tuy nhiên, làm thế nào chúng ta sẽ bao gồm các chỉ số NC? Tôi thậm chí không thể có được kích thước chính xác cho chỉ mục Clustered :)


Vì vậy, về bản chất, bạn có hai câu hỏi: (1) tại sao tổng độ dài hàng không khớp với kế toán của siêu dữ liệu về kích thước của toàn bộ bảng? Câu trả lời dưới đây giải quyết ít nhất một phần (và có thể dao động trên mỗi bản phát hành và mỗi tính năng, ví dụ như nén, cột, v.v.). Và quan trọng hơn: (2) làm thế nào bạn có thể xác định chính xác không gian thực tế được sử dụng cho mỗi bộ phận? Tôi không biết rằng bạn có thể làm điều đó một cách chính xác - bởi vì đối với một số dữ liệu được tính trong câu trả lời, không có cách nào để biết nó thuộc về bộ phận nào.
Aaron Bertrand

Tôi không nghĩ vấn đề là bạn không có kích thước chính xác cho chỉ mục được nhóm - siêu dữ liệu chắc chắn cho bạn biết chính xác bao nhiêu chỉ mục của bạn chiếm không gian. Những gì siêu dữ liệu không được thiết kế để nói với bạn - ít nhất là với thiết kế / cấu trúc hiện tại của bạn - bao nhiêu dữ liệu được liên kết với mỗi bộ phận.
Aaron Bertrand

Câu trả lời:


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.

Dữ liệu không phải là thứ duy nhất chiếm dung lượng trên trang dữ liệu 8k:

  • Có không gian dành riêng. Bạn chỉ được phép sử dụng 8060 trong số 8192 byte (đó là 132 byte không bao giờ là của bạn ở vị trí đầu tiên):

    • Tiêu đề trang: Đây chính xác là 96 byte.
    • Mảng vị trí: đây là 2 byte mỗi hàng và cho biết phần bù của mỗi hàng bắt đầu trên trang. Kích thước của mảng này không giới hạn ở 36 byte còn lại (132 - 96 = 36), nếu không, bạn sẽ bị giới hạn một cách hiệu quả khi chỉ đặt tối đa 18 hàng trên một trang dữ liệu. Điều này có nghĩa là mỗi hàng lớn hơn 2 byte so với bạn nghĩ. Giá trị này không được bao gồm trong "kích thước bản ghi" như được báo cáo bởi DBCC PAGE, đó là lý do tại sao nó được giữ riêng ở đây thay vì được bao gồm trong thông tin mỗi hàng bên dưới.
    • Dữ liệu meta Per-Row (bao gồm, nhưng không giới hạn):
      • Kích thước thay đổi tùy theo định nghĩa bảng (nghĩa là số cột, chiều dài thay đổi hoặc chiều dài cố định, v.v.). Thông tin được lấy từ ý kiến ​​của @ PaulWhite và @ Aaron có thể được tìm thấy trong cuộc thảo luận liên quan đến câu trả lời và thử nghiệm này.
      • Tiêu đề hàng: 4 byte, 2 trong số chúng biểu thị loại bản ghi và hai loại còn lại là phần bù cho Bitmap NULL
      • Số cột: 2 byte
      • NULL Bitmap: cột nào hiện tại NULL. 1 byte cho mỗi bộ 8 cột. Và cho tất cả các cột, ngay cả NOT NULLnhững người. Do đó, tối thiểu 1 byte.
      • Mảng bù có độ dài cột thay đổi: tối thiểu 4 byte. 2 byte để giữ số lượng cột có độ dài thay đổi và sau đó là 2 byte cho mỗi cột có độ dài thay đổi để giữ phần bù cho vị trí bắt đầu.
      • Thông tin phiên bản: 14 byte (điều này sẽ có mặt nếu cơ sở dữ liệu của bạn được đặt thành ALLOW_SNAPSHOT_ISOLATION ONhoặc READ_COMMITTED_SNAPSHOT ON).
    • Vui lòng xem Câu hỏi và Trả lời sau để biết thêm chi tiết về điều này: Slot Array và Tổng kích thước trang
    • Vui lòng xem bài đăng trên blog sau của Paul Randall, trong đó có một số chi tiết thú vị về cách các trang dữ liệu được trình bày: Trêu chọc với TRANG DBCC (Phần 1 của?)
  • Con trỏ LOB cho dữ liệu không được lưu trữ trong hàng. Vì vậy, nó sẽ chiếm DATALENGTH+ con trỏ. Nhưng đây không phải là một kích thước tiêu chuẩn. Vui lòng xem bài đăng trên blog sau để biết chi tiết về chủ đề phức tạp này: Kích thước của Con trỏ LOB cho các loại (MAX) như Varchar, Varbinary, Etc là gì? . Giữa bài đăng được liên kết đó và một số thử nghiệm bổ sung mà tôi đã thực hiện , các quy tắc (mặc định) phải như sau:

    • Legacy / phản đối loại LOB mà không ai nên sử dụng nữa như của SQL Server 2005 ( TEXT, NTEXTIMAGE):
      • Theo mặc định, luôn lưu trữ dữ liệu của họ trên các trang LOB và luôn sử dụng con trỏ 16 byte để lưu trữ LOB.
      • NẾU sp_tableoption đã được sử dụng để đặt text in rowtùy chọn, sau đó:
        • nếu có không gian trên trang để lưu trữ giá trị giá trị không lớn hơn kích thước liên tiếp tối đa (phạm vi có thể định cấu hình là 24 - 7000 byte với mặc định là 256), thì nó sẽ được lưu liên tiếp,
        • nếu không nó sẽ là một con trỏ 16 byte.
    • Đối với các loại LOB mới giới thiệu trong SQL Server 2005 ( VARCHAR(MAX), NVARCHAR(MAX)VARBINARY(MAX)):
      • Theo mặc định:
        • Nếu giá trị không lớn hơn 8000 byte có chỗ trên trang, thì nó sẽ được lưu liên tiếp.
        • Root nội tuyến - đối với dữ liệu trong khoảng từ 8001 đến 40.000 (thực sự là 42.000) byte, cho phép không gian, sẽ có 1 đến 5 con trỏ (24 - 72 byte) IN ROW trỏ trực tiếp vào (các) trang LOB. 24 byte cho trang LOB 8k ban đầu và 12 byte cho mỗi trang 8k bổ sung cho tối đa bốn trang 8k nữa.
        • TEXT_TREE - đối với dữ liệu trên 42.000 byte hoặc nếu 1 đến 5 con trỏ không khớp với nhau, thì sẽ chỉ có một con trỏ 24 byte đến trang bắt đầu của danh sách các con trỏ tới các trang LOB (tức là "text_tree " trang).
      • NẾU sp_tableoption đã được sử dụng để đặt large value types out of rowtùy chọn, sau đó luôn sử dụng con trỏ 16 byte để lưu trữ LOB.
    • Tôi đã nói quy tắc "mặc định" vì tôi không kiểm tra các giá trị liên tiếp chống lại tác động của một số tính năng nhất định như Nén dữ liệu, Mã hóa cấp cột, Mã hóa dữ liệu trong suốt, Luôn được mã hóa, v.v.
  • Các trang tràn LOB: Nếu một giá trị là 10k, thì điều đó sẽ yêu cầu 1 trang tràn 8k đầy đủ, và sau đó là một phần của trang thứ 2. Nếu không có dữ liệu nào khác có thể chiếm dung lượng còn lại (hoặc thậm chí được phép, tôi không chắc chắn về quy tắc đó), thì bạn có khoảng 6kb không gian "lãng phí" trên kho dữ liệu tràn LOB thứ 2 đó.

  • Dung lượng không sử dụng: Một trang dữ liệu 8k chỉ có thế: 8192 byte. Nó không thay đổi kích thước. Tuy nhiên, dữ liệu và siêu dữ liệu được đặt trên nó, không phải lúc nào cũng vừa vặn với tất cả 8192 byte. Và các hàng không thể được chia thành nhiều trang dữ liệu. Vì vậy, nếu bạn còn 100 byte nhưng không có hàng nào (hoặc không có hàng nào phù hợp với vị trí đó, tùy thuộc vào một số yếu tố), trang dữ liệu vẫn chiếm 8192 byte và truy vấn thứ 2 của bạn chỉ đếm số lượng trang dữ liệu. Bạn có thể tìm thấy giá trị này ở hai nơi (chỉ cần lưu ý rằng một phần của giá trị này là một phần của không gian dành riêng đó):

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;Tìm kiếm ParentObject= "TRANG ĐẦU:" và Field= "m_freeCnt". Các Valuelĩnh vực là số byte không sử dụng.
    • 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;Đây là cùng một giá trị như được báo cáo bởi "m_freeCnt". Điều này dễ hơn DBCC vì nó có thể nhận được nhiều trang, nhưng cũng yêu cầu các trang đã được đọc vào nhóm bộ đệm ở vị trí đầu tiên.
  • Dung lượng được dành riêng bởi FILLFACTOR<100. Các trang mới được tạo không tôn trọng FILLFACTORcài đặt, nhưng thực hiện REBUILD sẽ dành chỗ đó trên mỗi trang dữ liệu. Ý tưởng đằng sau không gian dành riêng là nó sẽ được sử dụng bởi các chèn và / hoặc cập nhật không tuần tự mở rộng kích thước của các hàng trên trang, do các cột có chiều dài thay đổi được cập nhật với dữ liệu nhiều hơn một chút (nhưng không đủ để gây ra tách trang). Nhưng bạn có thể dễ dàng dự trữ không gian trên các trang dữ liệu tự nhiên sẽ không bao giờ nhận được các hàng mới và không bao giờ có các hàng hiện có được cập nhật hoặc ít nhất là không được cập nhật theo cách làm tăng kích thước của hàng.

  • Chia trang (phân mảnh): Cần thêm một hàng vào một vị trí không có chỗ cho hàng sẽ gây ra sự phân chia trang. Trong trường hợp này, khoảng 50% dữ liệu hiện có được chuyển sang một trang mới và hàng mới được thêm vào một trong 2 trang. Nhưng bây giờ bạn có thêm một chút không gian trống mà không được DATALENGTHtính toán.

  • Hàng được đánh dấu để xóa. Khi bạn xóa các hàng, chúng không phải lúc nào cũng bị xóa ngay lập tức khỏi trang dữ liệu. Nếu chúng không thể bị xóa ngay lập tức, chúng sẽ được "đánh dấu cho cái chết" (tham chiếu của Steven Segal) và sẽ bị xóa về mặt vật lý sau quá trình dọn dẹp ma (tôi tin đó là tên). Tuy nhiên, những điều này có thể không liên quan đến Câu hỏi cụ thể này.

  • Trang ma? Không chắc đó có phải là thuật ngữ phù hợp hay không, nhưng đôi khi các trang dữ liệu không bị xóa cho đến khi hoàn thành REBUILD của Chỉ mục cụm. Điều đó cũng sẽ chiếm nhiều trang hơn so DATALENGTHvới. Điều này thường không nên xảy ra, nhưng tôi đã gặp phải nó một lần, vài năm trước.

  • Các cột SPARSE: Các cột thưa thớt tiết kiệm không gian (chủ yếu cho các kiểu dữ liệu có độ dài cố định) trong các bảng trong đó một% lớn các hàng NULLdành cho một hoặc nhiều cột. Các SPARSEtùy chọn làm cho các NULLkiểu giá trị lên 0 byte (thay vì số tiền cố định chiều dài bình thường, chẳng hạn như 4 byte cho một INT), nhưng , không NULL giá trị từng cất lên thêm 4 byte với nhiều loại chiều dài cố định và một số lượng biến cho các loại chiều dài thay đổi. Vấn đề ở đây là DATALENGTHkhông bao gồm 4 byte bổ sung cho các giá trị không phải NULL trong cột SPARSE, vì vậy 4 byte đó cần được thêm lại. Bạn có thể kiểm tra xem có bất kỳ SPARSEcột nào thông qua:

    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;

    Và sau đó cho mỗi SPARSEcột, cập nhật truy vấn ban đầu để sử dụng:

    SUM(DATALENGTH(FieldN) + 4)

    Xin lưu ý rằng phép tính ở trên để thêm vào 4 byte tiêu chuẩn là một chút đơn giản vì nó chỉ hoạt động đối với các loại có độ dài cố định. VÀ, có thêm dữ liệu meta trên mỗi hàng (từ những gì tôi có thể nói cho đến nay) làm giảm không gian có sẵn cho dữ liệu, chỉ bằng cách có ít nhất một cột SPARSE. Để biết thêm chi tiết, vui lòng xem trang MSDN để sử dụng Cột thưa .

  • Các trang Index và các trang khác (ví dụ: IAM, PFS, GAM, SGAM, v.v.): đây không phải là các trang "dữ liệu" về mặt dữ liệu người dùng. Chúng sẽ làm tăng tổng kích thước của bảng. Nếu sử dụng SQL Server 2012 hoặc mới hơn, bạn có thể sử dụng sys.dm_db_database_page_allocationsChức năng quản lý động (DMF) để xem các loại trang (phiên bản trước của SQL Server có thể sử dụng 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

    Cả mệnh đề WHERE DBCC INDcũng không sys.dm_db_database_page_allocations(sẽ có báo cáo bất kỳ trang Index nào và chỉ DBCC INDbáo cáo sẽ có ít nhất một trang IAM.

  • DATA_COMPRESSION: Nếu bạn đã bật ROWhoặc PAGENén trên Chỉ mục cụm hoặc Heap, thì bạn có thể quên hầu hết những gì đã được đề cập cho đến nay. Tiêu đề trang 96 byte, Mảng slot 2 byte mỗi hàng và Thông tin phiên bản 14 byte mỗi hàng vẫn còn đó, nhưng biểu diễn vật lý của dữ liệu trở nên rất phức tạp (nhiều hơn so với những gì đã được đề cập khi Nén không được sử dụng). Ví dụ: với Nén hàng, SQL Server cố gắng sử dụng bộ chứa nhỏ nhất có thể để phù hợp với từng cột, trên mỗi hàng. Vì vậy, nếu bạn có một BIGINTcột khác (giả sử SPARSEcũng không được bật) luôn chiếm 8 byte, nếu giá trị nằm trong khoảng từ -128 đến 127 (tức là số nguyên 8 bit đã ký) thì nó sẽ chỉ sử dụng 1 byte và nếu giá trị có thể phù hợp với mộtSMALLINT, nó sẽ chỉ mất 2 byte. Các kiểu số nguyên NULLhoặc 0chiếm không gian và được chỉ định đơn giản là NULLhoặc "trống" (nghĩa là 0) trong một mảng ánh xạ ra các cột. Và còn nhiều, rất nhiều quy tắc khác. Có dữ liệu Unicode ( NCHAR, NVARCHAR(1 - 4000)nhưng không NVARCHAR(MAX) , ngay cả khi được lưu liên tiếp)? Nén nén Unicode đã được thêm vào trong SQL Server 2008 R2, nhưng không có cách nào để dự đoán kết quả của giá trị "nén" trong mọi tình huống mà không thực hiện nén thực tế do sự phức tạp của các quy tắc .

Vì vậy, thực sự, truy vấn thứ hai của bạn, trong khi chính xác hơn về tổng dung lượng vật lý chiếm trên đĩa, chỉ thực sự chính xác khi thực hiện một REBUILDChỉ số cụm. Và sau đó, bạn vẫn cần tính đến bất kỳ FILLFACTORcài đặt nào dưới 100. Và thậm chí sau đó luôn có các tiêu đề trang và thường đủ một lượng không gian "lãng phí" mà không thể lấp đầy do quá nhỏ để phù hợp với bất kỳ hàng nào trong này bảng, hoặc ít nhất là hàng hợp lý nên đi trong khe đó.

Về tính chính xác của truy vấn thứ 2 trong việc xác định "mức sử dụng dữ liệu", có vẻ công bằng nhất khi sao lưu các byte Tiêu đề trang vì chúng không sử dụng dữ liệu: chúng là chi phí kinh doanh. Nếu có 1 hàng trên một trang dữ liệu và hàng đó chỉ là một TINYINT, thì 1 byte đó vẫn yêu cầu trang dữ liệu tồn tại và do đó 96 byte của tiêu đề. 1 bộ phận đó có nên bị tính phí cho toàn bộ trang dữ liệu không? Nếu trang dữ liệu đó sau đó được Bộ số 2 điền vào, họ sẽ chia đều chi phí "phí" đó hay trả theo tỷ lệ? Có vẻ dễ nhất để chỉ cần sao lưu nó ra. Trong trường hợp đó, sử dụng giá trị 8để nhân lên number of pageslà quá cao. Làm thế nào về:

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

Do đó, sử dụng một cái gì đó như:

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

cho tất cả các tính toán đối với các cột "number_of_pages".

, xem xét rằng việc sử dụng DATALENGTHmỗi trường không thể trả về siêu dữ liệu trên mỗi hàng, nên được thêm vào truy vấn trên mỗi bảng của bạn, nơi bạn nhận được DATALENGTHmỗi trường, lọc trên mỗi "bộ phận":

  • Loại bản ghi và bù vào Bitmap NULL: 4 byte
  • Đếm cột: 2 byte
  • Slot Array: 2 byte (không bao gồm trong "kích thước bản ghi" nhưng vẫn cần tính đến)
  • Bitmap NULL: 1 byte cho mỗi 8 cột (cho tất cả các cột)
  • Phiên bản hàng: 14 byte (nếu cơ sở dữ liệu có ALLOW_SNAPSHOT_ISOLATIONhoặc READ_COMMITTED_SNAPSHOTđược đặt thành ON)
  • Cột có độ dài thay đổi Mảng bù: 0 byte nếu tất cả các cột có độ dài cố định. Nếu bất kỳ cột nào có độ dài thay đổi, thì 2 byte, cộng với 2 byte cho mỗi cột chỉ có chiều dài thay đổi.
  • Con trỏ LOB: phần này rất thiếu chính xác vì sẽ không có con trỏ nếu giá trị là NULLvà nếu giá trị khớp với hàng thì nó có thể nhỏ hơn hoặc lớn hơn nhiều so với con trỏ và nếu giá trị được lưu trữ ngoài- hàng, sau đó kích thước của con trỏ có thể phụ thuộc vào lượng dữ liệu có. Tuy nhiên, vì chúng tôi chỉ muốn một ước tính (tức là "swag"), có vẻ như 24 byte là một giá trị tốt để sử dụng (tốt, tốt như bất kỳ loại nào khác ;-). Đây là mỗi MAXlĩnh vực.

Do đó, sử dụng một cái gì đó như:

  • Nói chung (tiêu đề hàng + số cột + mảng khe + bitmap NULL):

    ([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
  • Nói chung (tự động phát hiện nếu có "thông tin phiên bản"):

    + (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())
  • NẾU có bất kỳ cột có chiều dài thay đổi, sau đó thêm:

    + 2 + (2 * {NumVariableLengthColumns})
  • NẾU có bất kỳ MAXcột / LOB nào, sau đó thêm:

    + (24 * {NumLobColumns})
  • Nói chung:

    )) AS [MetaDataBytes]

Điều này không chính xác và một lần nữa sẽ không hoạt động nếu bạn bật Row hoặc Page nén trên Heap hoặc Clustered Index, nhưng chắc chắn sẽ giúp bạn tiến gần hơn.


CẬP NHẬT về Bí ẩn khác biệt 15%

Chúng tôi (bao gồm cả tôi) đã rất tập trung vào việc suy nghĩ về cách các trang dữ liệu được trình bày và làm thế nào DATALENGTHcó thể giải thích cho những thứ mà chúng tôi đã không dành nhiều thời gian để xem xét truy vấn thứ hai. Tôi đã chạy truy vấn đó với một bảng duy nhất và sau đó so sánh các giá trị đó với những gì được báo cáo sys.dm_db_database_page_allocationsvà chúng không phải là cùng một giá trị cho số lượng trang. Trong một linh cảm, tôi đã loại bỏ các hàm tổng hợp GROUP BYvà thay thế SELECTdanh sách bằng a.*, '---' AS [---], p.*. Và sau đó, mọi thứ trở nên rõ ràng: mọi người phải cẩn thận khi những người đan xen âm u này nhận được thông tin và tập lệnh của họ từ ;-). Truy vấn thứ 2 được đăng trong Câu hỏi không chính xác, đặc biệt đối với Câu hỏi cụ thể này.

  • Vấn đề nhỏ: bên ngoài nó không có ý nghĩa nhiều GROUP BY rows(và không có cột đó trong hàm tổng hợp), THAM GIA giữa sys.allocation_unitssys.partitionskhông đúng về mặt kỹ thuật. Có 3 loại Đơn vị phân bổ và một trong số chúng sẽ THAM GIA vào một lĩnh vực khác. Khá thường xuyên partition_idhobt_idgiống nhau, vì vậy có thể không bao giờ có vấn đề, nhưng đôi khi hai trường đó có giá trị khác nhau.

  • Vấn đề chính: truy vấn sử dụng used_pagestrường. Trường đó bao gồm tất cả các loại trang: Dữ liệu, Chỉ mục, IAM, v.v., tc. Có một trường khác, phù hợp hơn để sử dụng khi chỉ liên quan đến dữ liệu thực tế : data_pages.

Tôi đã điều chỉnh truy vấn thứ 2 trong Câu hỏi với các mục ở trên và sử dụng kích thước trang dữ liệu sao lưu tiêu đề trang. Tôi cũng lấy ra hai câu lệnh JOIN đó là không cần thiết: sys.schemas(thay thế bằng cuộc gọi đến SCHEMA_NAME()), và sys.indexes(các Clustered Index luôn index_id = 1và chúng tôi có index_idtrong 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;

Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Paul White 9

Mặc dù truy vấn cập nhật mà bạn cung cấp cho truy vấn thứ 2 thậm chí còn xa hơn (theo hướng khác bây giờ :)), tôi vẫn ổn với câu trả lời này. Đây là một điều rất khó để bẻ khóa rõ ràng và với những gì đáng giá, tôi rất vui ngay cả với các chuyên gia giúp tôi, tôi vẫn không thể tìm ra lý do chính xác hai phương pháp không khớp nhau. Tôi sẽ chỉ sử dụng phương pháp luận trong câu trả lời khác để ngoại suy. Tôi ước tôi có thể bỏ phiếu đồng ý cho cả hai câu trả lời này, nhưng @srutzky đã hỗ trợ tất cả các lý do tại sao hai câu trả lời sẽ bị tắt.
Chris Woods

6

Có thể đây là một câu trả lời grunge nhưng đây là những gì tôi sẽ làm.

Vì vậy, DATALENGTH chỉ chiếm 86% tổng số. Nó vẫn rất chia đại diện. Chi phí trong câu trả lời xuất sắc từ srutzky nên có sự phân chia khá đồng đều.

Tôi sẽ sử dụng truy vấn thứ hai của bạn (các trang) cho tổng số. Và sử dụng đầu tiên (datalength) để phân bổ phân chia. Nhiều chi phí được phân bổ bằng cách sử dụng một chuẩn hóa.

Và bạn phải xem xét một câu trả lời gần hơn sẽ tăng chi phí, do đó, ngay cả các bộ phận bị mất khi chia tách vẫn có thể trả nhiều hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.