sys.allocation_units의 테이블 크기와 일치하지 않는 DATALENGTH의 합


11

DATALENGTH()테이블의 모든 레코드에 대한 모든 필드의 합계를 합하면 테이블의 전체 크기를 얻는다 는 인상을 받았습니다 . 내가 착각 했니?

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

데이터베이스에서 특정 테이블의 크기를 얻기 위해 아래 쿼리 (온라인에서 가져온 테이블 크기, 클러스터 된 인덱스 만 NC 인덱스를 포함하지 않음)를 사용했습니다. 청구 목적을 위해 (우리는 사용 된 공간의 양에 따라 부서에 요금을 청구합니다)이 표에서 각 부서가 사용한 공간의 양을 알아 내야합니다. 테이블 내의 각 그룹을 식별하는 쿼리가 있습니다. 각 그룹이 얼마나 많은 공간을 차지하고 있는지 파악해야합니다.

VARCHAR(MAX)테이블의 필드 로 인해 행당 공간이 크게 흔들릴 수 있으므로 부서의 평균 행 * 비율을 취할 수는 없습니다. DATALENGTH()위에서 설명한 접근 방식을 사용하면 아래 쿼리에 사용 된 총 공간의 85 % 만 얻습니다. 생각?

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

각 부서에 대해 필터링 된 인덱스를 생성하거나 테이블을 분할하여 인덱스 당 사용 된 공간을 직접 쿼리 할 것을 제안했습니다. 필터링 된 인덱스는 공간을 항상 사용하는 대신 프로그래밍 방식으로 만들 수 있습니다 (유지 보수 기간 동안 또는 정기적 인 청구를 수행해야 할 때 다시 삭제) (파티션은이 점에서 더 좋습니다).

나는 그 제안을 좋아하고 일반적으로 그렇게 할 것입니다. 그러나 솔직히 말해서 "각 부서"를 예제로 사용하여 왜 이것이 필요한지 설명하지만 솔직히 말하면 이것이 실제로는 아닙니다. 기밀성으로 인해이 데이터가 필요한 정확한 이유를 설명 할 수는 없지만 다른 부서와 유사합니다.

이 테이블의 비 클러스터형 인덱스와 관련하여 : NC 인덱스의 크기를 얻을 수 있다면 좋을 것입니다. 그러나 NC 인덱스는 클러스터 인덱스 크기의 <1 %를 차지하므로 포함하지 않는 것이 좋습니다. 그러나 어쨌든 NC 인덱스를 어떻게 포함합니까? 클러스터 된 인덱스의 정확한 크기조차 얻을 수 없습니다 :)


따라서 본질적으로 두 가지 질문이 있습니다. (1) 행 길이의 합이 전체 테이블 크기의 메타 데이터 계정과 일치하지 않는 이유는 무엇입니까? 아래의 답변은 적어도 부분적으로 (및 압축, 열 저장소 등의 릴리스 및 기능마다 변동될 수있는) 문제를 해결합니다. 더 중요한 것은 : (2) 부서 당 실제 사용 된 공간을 어떻게 정확하게 결정할 수 있습니까? 나는 당신이 정확하게 그렇게 할 수 있다는 것을 모른다. 왜냐하면 대답에 설명 된 일부 데이터의 경우 어느 부서가 속해 있는지 알 수있는 방법이 없기 때문이다.
Aaron Bertrand

문제는 클러스터 된 인덱스의 정확한 크기가 없다는 것입니다. 메타 데이터는 인덱스가 차지하는 공간의 양을 정확하게 알려줍니다. 메타 데이터가 적어도 현재의 디자인 / 구조를 고려하여 각 부서와 연관된 데이터의 양을 알려주기 위해 설계되지 않은 것.
Aaron Bertrand

답변:


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 데이터 페이지에서 공간을 차지하는 유일한 것은 아닙니다.

  • 예약 된 공간이 있습니다. 8192 바이트 중 8060 개만 사용할 수 있습니다 (처음에는 자신의 바이트가 아닌 132 바이트).

    • 페이지 헤더 : 정확히 96 바이트입니다.
    • 슬롯 배열 : 이것은 행당 2 바이트이며 페이지에서 각 행이 시작되는 위치의 오프셋을 나타냅니다. 이 배열의 크기는 남은 36 바이트 (132-96 = 36)로 제한되지 않으며, 데이터 페이지에 최대 18 개의 행만 넣는 것으로 효과적으로 제한됩니다. 즉, 각 행이 생각보다 2 바이트 더 큽니다. 이 값은에 의해보고 된 "레코드 크기"에 포함되지 않으므로 DBCC PAGE아래의 행별 정보에 포함되지 않고 여기에서 별도로 유지됩니다.
    • 행별 메타 데이터 (단, 이에 한하지 않음) :
      • 크기는 테이블 정의 (예 : 열 수, 가변 길이 또는 고정 길이 등)에 따라 다릅니다. 이 답변 및 테스트와 관련된 토론 에서 찾을 수있는 @ PaulWhite 's 및 @Aaron의 의견에서 가져온 정보 .
      • 행 헤더 : 4 바이트, 그 중 2 개는 레코드 유형을 나타내고 나머지 2 개는 NULL 비트 맵에 대한 오프셋
      • 열 수 : 2 바이트
      • NULL 비트 맵 : 현재 어떤 열 NULL입니다. 8 열의 각 세트당 1 바이트. 그리고 모든 열, 심지어 열에도 적용 NOT NULL됩니다. 따라서 최소 1 바이트입니다.
      • 가변 길이 열 오프셋 배열 : 최소 4 바이트 가변 길이 열 수를 보유하려면 2 바이트, 각 가변 길이 열당 2 바이트를 사용하여 오프셋을 시작 위치까지 보유하십시오.
      • 버전 정보 : 14 바이트 (데이터베이스가 ALLOW_SNAPSHOT_ISOLATION ON또는 로 설정된 경우 READ_COMMITTED_SNAPSHOT ON표시됨)
    • 이에 대한 자세한 내용은 다음 질문과 답변을 참조하십시오. 슬롯 배열 및 총 페이지 크기
    • 배치하는 방법을 데이터 페이지에 몇 가지 흥미로운 세부 사항을 가지고 바울 랜달에서 다음 블로그 게시물을 참조하십시오 : (? 1 부) 파고에 대한 DBCC PAGE로를
  • 행에 저장되지 않은 데이터에 대한 LOB 포인터. 그래서 그것은 DATALENGTH+ pointer_size를 설명합니다. 그러나 이것들은 표준 크기가 아닙니다. 이 복잡한 주제에 대한 자세한 내용은 다음 블로그 게시물을 참조하십시오. Varchar, Varbinary 등과 같은 (MAX) 유형의 LOB 포인터 크기는 얼마입니까? . 연결된 게시물과 내가 수행 한 추가 테스트 사이 에서 (기본) 규칙은 다음과 같아야합니다.

    • 레거시 / 아무도 (SQL 서버 2005로 더 이상 사용되지 않는다는 것을 LOB 유형을 사용되지 않는 TEXT, NTEXT그리고 IMAGE) :
      • 기본적으로 항상 데이터를 LOB 페이지에 저장하고 항상 LOB 스토리지에 대한 16 바이트 포인터를 사용하십시오.
      • IF의 sp_tableoption은 세트에 사용 된 text in row후, 옵션 :
        • 페이지에 값을 저장할 공간이 있고 값이 최대 행 내 크기 (기본값은 256-24-7000 바이트 범위)보다 크지 않으면 행에 저장됩니다.
        • 그렇지 않으면 16 바이트 포인터가됩니다.
    • SQL Server 2005에서 도입 된 새로운 LOB 유형의 ( VARCHAR(MAX), NVARCHAR(MAX), 및 VARBINARY(MAX)) :
      • 기본적으로:
        • 값보다 큰 8000 바이트하지, 경우 페이지의 여지가, 다음은에 열 저장됩니다.
        • 인라인 루트 — 8001-40,000 (실제 42,000) 바이트 사이의 데이터에 대해 공간을 허용하며 LOB 페이지를 직접 가리키는 1-5 개의 포인터 (24-72 바이트) 행이 있습니다. 초기 8k LOB 페이지의 경우 24 바이트, 추가 8k 페이지 당 최대 12 개의 8k 페이지의 경우 12 바이트.
        • TEXT_TREE — 42,000 바이트를 초과하는 데이터의 경우 또는 1-5 개의 포인터가 행에 맞지 않으면 LOB 페이지에 대한 포인터 목록의 시작 페이지에 대한 24 바이트 포인터 만 있습니다 (예 : "text_tree "페이지).
      • 경우 sp_tableoption이 세트에 사용 된 large value types out of row옵션을 다음 항상 LOB 저장에 16 바이트 포인터를 사용합니다.
    • 나는 않았다 "기본"규칙 때문에 고 말했다 하지 등의 데이터 압축, 열 수준의 암호화, 투명한 데이터 암호화, 항상 암호화와 같은 특정 기능에 미치는 영향에 대해 행 내부의 값을 테스트
  • LOB 오버 플로우 페이지 : 값이 10k 인 경우 전체 8k 페이지의 오버 플로우 1 개가 필요하고 두 번째 페이지의 일부가 필요합니다. 다른 데이터가 나머지 공간을 차지할 수없는 경우 (또는 해당 규칙을 확신 할 수없는 경우) 해당 두 번째 LOB 오버 플로우 데이터 페이지에 약 6kb의 "소비 된"공간이 있습니다.

  • 사용되지 않은 공간 : 8k 데이터 페이지는 8192 바이트입니다. 크기는 변하지 않습니다. 그러나 데이터와 메타 데이터가 항상 8192 바이트에 모두 적합하지는 않습니다. 행을 여러 데이터 페이지로 분할 할 수 없습니다. 따라서 100 바이트가 남아 있지만 행이 없거나 여러 요인에 따라 해당 위치에 맞는 행이 없으면 데이터 페이지가 여전히 8192 바이트를 차지하고 두 번째 쿼리는 데이터 페이지. 이 값은 두 곳에서 찾을 수 있습니다 (이 값의 일부는 예약 된 공간의 일부임을 명심하십시오).

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;를 찾으 ParentObject= "페이지 헤더"와 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 %가 새 페이지로 이동하고 새 행이 두 페이지 중 하나에 추가됩니다. 그러나 이제 DATALENGTH계산 에 의해 계산 되지 않은 약간 더 많은 여유 공간이 있습니다.

  • 삭제 표시된 행. 행을 삭제할 때 항상 데이터 페이지에서 행이 제거되는 것은 아닙니다. 즉시 제거 할 수 없으면 "사망으로 표시"(Steven Segal 참조)되고 나중에 유령 정리 프로세스에 의해 물리적으로 제거됩니다 (저는 그 이름이라고 생각합니다). 그러나 이것들은이 특정 질문과 관련이 없을 수도 있습니다.

  • 고스트 페이지? 이것이 적절한 용어인지 확실하지 않지만 때때로 클러스터형 인덱스의 REBUILD가 완료 될 때까지 데이터 페이지가 제거되지 않습니다. 그것은 또한 DATALENGTH합산되는 것보다 많은 페이지를 설명 할 것 입니다. 이것은 일반적으로 발생해서는 안되지만 몇 년 전에 한 번 실행되었습니다.

  • SPARSE 컬럼 : 스파 스 컬럼은 행의 큰 %가 NULL하나 이상의 컬럼에 대한 테이블에서 공간 (주로 고정 길이 데이터 유형의 경우)을 절약합니다 . 이 SPARSE옵션은 NULL값 유형을 0 바이트 (예 : 4 바이트와 같은 일반 고정 길이 대신 INT)로 설정 하지만 NULL이 아닌 값은 각각 고정 길이 유형의 경우 추가 4 바이트와 가변 길이 유형. 여기서 문제 DATALENGTH는 SPARSE 열에 NULL이 아닌 값에 대한 추가 4 바이트를 포함하지 않으므로 해당 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 바이트를 추가하기위한 위의 계산은 고정 길이 유형에서만 작동하므로 약간 단순합니다. 그리고 적어도 하나의 SPARSE 열을 가짐으로써 데이터에 사용 가능한 공간을 줄이는 행 당 추가 메타 데이터가 있습니다 (지금까지 알 수있는 것부터). 자세한 내용은 스파 스 열 사용에 대한 MSDN 페이지를 참조하십시오 .

  • 인덱스 및 기타 (예 : IAM, PFS, GAM, SGAM 등) 페이지 : 사용자 데이터 측면에서 "데이터"페이지가 아닙니다. 이것들은 테이블의 전체 크기를 팽창시킵니다. SQL Server 2012 이상을 사용하는 경우 sys.dm_db_database_page_allocationsDMF (Dynamic Management Function)를 사용하여 페이지 유형을 볼 수 있습니다 (이전 버전의 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 INDsys.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. 그리고 많은 다른 규칙들이 있습니다. 유무 유니 코드 데이터 ( NCHAR, NVARCHAR(1 - 4000)하지만, 하지 NVARCHAR(MAX) 에 열 저장하는 경우에도)? 유니 코드 압축은 SQL Server 2008 R2에 추가되었지만 규칙 의 복잡성을 고려하여 실제 압축을 수행하지 않고 모든 상황에서 "압축 된"값의 결과를 예측할 수있는 방법은 없습니다 .

따라서 실제로 두 번째 쿼리는 디스크에서 차지하는 총 물리적 공간 측면에서 더 정확 REBUILD하지만 클러스터형 인덱스를 수행 할 때만 정확합니다 . 그리고 그 후에도 여전히 FILLFACTOR100 미만의 설정 을 고려해야 합니다. 심지어 페이지 헤더가 항상 있고,이 행에 맞추기에는 너무 작아서 채울 수없는 "소비"공간이 충분합니다. 테이블 또는 최소한 해당 슬롯에 논리적으로 들어가야하는 행.

"데이터 사용량"을 결정할 때 두 번째 쿼리의 정확성과 관련하여 페이지 헤더 바이트는 데이터 사용량이 아니기 때문에 백 아웃하는 것이 가장 공정한 것 같습니다. 비즈니스 비용 오버 헤드입니다. 데이터 페이지에 1 개의 행이 있고 해당 행이 단지 1 TINYINT인 경우 해당 1 바이트는 여전히 데이터 페이지가 존재해야하므로 헤더의 96 바이트가 필요합니다. 해당 부서에서 전체 데이터 페이지에 대한 비용을 청구해야합니까? 해당 데이터 페이지가 부서 # 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"열에 대한 모든 계산

AND , DATALENGTH각 필드 당 을 사용 하면 행당 메타 데이터를 리턴 할 수 없다는 것을 고려하여 DATALENGTH, 각 "부서"에 대해 필터링하여 각 필드 를 얻는 테이블 당 쿼리에 추가해야합니다 .

  • 레코드 유형 및 NULL에 대한 오프셋 비트 맵 : 4 바이트
  • 열 수 : 2 바이트
  • 슬롯 배열 : 2 바이트 ( "레코드 크기"에는 포함되지 않지만 여전히 설명해야 함)
  • NULL 비트 맵 : 8 열당 1 바이트 ( 모든 열)
  • 행 버전 관리 : 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두 번째 쿼리를 검토하는 데 많은 시간을 소비하지 않은 것들을 설명 할 수있는 방법에 대해 생각하는 데 집중했습니다 . 단일 테이블에 대해 해당 쿼리를 실행 한 다음 해당 값을보고 대상과 비교했는데 sys.dm_db_database_page_allocations페이지 수에 대해 동일한 값이 아니 었습니다. 직감에 집계 함수 및를 제거 GROUP BY하고 SELECT목록을 로 바꿨습니다 a.*, '---' AS [---], p.*. 그리고 다음 이 분명 해졌다 :이 어두운 인터 웹에 그들로부터 자신의 정보와 스크립트 어디서 사람들이주의해야합니다 ;-). 질문에 게시 된 두 번째 쿼리는 특히이 특정 질문에 대해 정확하지 않습니다.

  • 사소한 문제 : 그것은에 많은 감각을하지 않는 외부는 GROUP BY rows(가) 사이에 가입 (및 집계 함수에 해당 열이없는) sys.allocation_unitssys.partitions기술적으로 정확하지 않습니다. 할당 단위에는 3 가지 유형이 있으며 그 중 하나는 다른 필드에 참여해야합니다. 자주 partition_id그리고 hobt_id동일하므로 문제가 없을 수 있지만 때로는이 두 필드의 값이 다릅니다.

  • 주요 문제 : 쿼리가 used_pages필드를 사용합니다 . 이 필드에는 모든 유형의 페이지 (데이터, 색인, IAM 등)가 포함됩니다. 실제 데이터에만 관심이있을 때 사용해야하는 다른 적절한 필드가 있습니다 data_pages.

위의 항목을 염두에두고 페이지 머리글을 뒷받침하는 데이터 페이지 크기를 사용하여 질문의 두 번째 쿼리를 수정했습니다. : 나는 또한 두가 불필요한했다 조인 제거 sys.schemas(에 대한 호출로 대체 SCHEMA_NAME()) 및 sys.indexes(클러스터 된 인덱스는 항상 index_id = 1우리는이 index_idsys.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;

의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Paul White 9

두 번째 쿼리에 제공 한 업데이트 된 쿼리가 훨씬 더 떨어져 있지만 (지금 다른 방향으로) : 나는이 대답에 동의합니다. 이것은 분명히 깨지기 힘든 너트이며 가치가있는 것에 대해, 나는 두 방법이 일치하지 않는 정확한 이유를 여전히 알 수없는 전문가조차도 기쁘다. 외삽 법에 대한 다른 답변에서 방법론을 사용하려고합니다. 이 답변 모두에 대해 찬성 투표를 할 수 있기를 바랍니다.하지만 @srutzky는 두 사람이 모두 탈퇴하는 모든 이유를 도와주었습니다.
Chris Woods

6

어쩌면 이것은 답이 아니지만 이것이 내가 할 일입니다.

따라서 DATALENGTH는 전체의 86 % 만 차지합니다. 여전히 매우 대표적인 분열입니다. srutzky의 탁월한 답변의 오버 헤드는 꽤 나뉘어 있어야합니다.

나는 두 번째 쿼리 (페이지)를 합계로 사용합니다. 그리고 분할을 할당하기 위해 첫 번째 (데이터 길이)를 사용하십시오. 많은 비용이 정규화를 사용하여 할당됩니다.

그리고 더 가까운 대답은 비용을 올릴 것이라는 점을 고려해야하므로 분할에서 잃어버린 부서조차도 더 많은 돈을 지불 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.