I tentativi di recuperare spazio inutilizzato fanno aumentare significativamente lo spazio utilizzato in SQL Server


15

Ho una tabella in un database di produzione che ha una dimensione di 525 GB, di cui 383 GB non utilizzati:

Spazio inutilizzato

Vorrei recuperare un po 'di questo spazio, ma, prima di fare confusione con il DB di produzione, sto testando alcune strategie su una tabella identica in un DB di test con meno dati. Questa tabella presenta un problema simile:

Spazio inutilizzato

Alcune informazioni sulla tabella:

  • Il fattore di riempimento è impostato su 0
  • Ci sono circa 30 colonne
  • Una delle colonne è un LOB di tipo image e contiene file di dimensioni variabili da pochi KB a diverse centinaia di MB
  • Alla tabella non sono associati indici ipotetici

Il server esegue SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64). Il database utilizza il SIMPLEmodello di recupero.

Alcune cose che ho provato:

  • Ricostruire gli indici: ALTER INDEX ALL ON dbo.MyTable REBUILD. Ciò ha avuto un impatto trascurabile.
  • Riorganizzazione degli indici: ALTER INDEX ALL ON dbo.MyTable REORGANIZE WITH(LOB_COMPACTION = ON). Ciò ha avuto un impatto trascurabile.
  • Copiato la colonna LOB in un'altra tabella, trascinata la colonna, ricreata la colonna e copiato nuovamente i dati (come indicato in questo post: Liberare la tabella SQL Server dello spazio inutilizzato ). Ciò ha ridotto lo spazio inutilizzato, ma sembrava convertirlo in spazio usato:

    Spazio inutilizzato

  • Utilizzato l'utilità bcp per esportare la tabella, troncarla e ricaricarla (come indicato in questo post: Come liberare lo spazio inutilizzato per una tabella ). Ciò ha anche ridotto lo spazio inutilizzato e aumentato lo spazio utilizzato in misura simile all'immagine sopra.

  • Anche se non è raccomandato, ho provato i comandi DBCC SHRINKFILE e DBCC SHRINKDATABASE, ma non hanno avuto alcun impatto sullo spazio inutilizzato.
  • Correre DBCC CLEANTABLE('myDB', 'dbo.myTable')non ha fatto differenza
  • Ho provato tutto quanto sopra sia mantenendo i tipi di dati di immagine e testo e dopo aver modificato i tipi di dati in varbinary (max) e varchar (max).
  • Ho provato a importare i dati in una nuova tabella in un nuovo database e anche questo ha convertito lo spazio non utilizzato in spazio utilizzato. Ho delineato i dettagli di questo tentativo in questo post .

Non voglio fare questi tentativi sul DB di produzione se questi sono i risultati che posso aspettarmi, quindi:

  1. Perché lo spazio inutilizzato viene appena convertito in spazio utilizzato dopo alcuni di questi tentativi? Mi sento come se non avessi una buona comprensione di ciò che sta accadendo sotto il cofano.
  2. C'è qualcos'altro che posso fare per ridurre lo spazio inutilizzato senza aumentare lo spazio utilizzato?

EDIT: ecco il rapporto sull'utilizzo del disco e lo script per la tabella:

Uso del disco

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MyTable](
    [Column1]  [int] NOT NULL,
    [Column2]  [int] NOT NULL,
    [Column3]  [int] NOT NULL,
    [Column4]  [bit] NOT NULL,
    [Column5]  [tinyint] NOT NULL,
    [Column6]  [datetime] NULL,
    [Column7]  [int] NOT NULL,
    [Column8]  [varchar](100) NULL,
    [Column9]  [varchar](256) NULL,
    [Column10] [int] NULL,
    [Column11] [image] NULL,
    [Column12] [text] NULL,
    [Column13] [varchar](100) NULL,
    [Column14] [varchar](6) NULL,
    [Column15] [int] NOT NULL,
    [Column16] [bit] NOT NULL,
    [Column17] [datetime] NULL,
    [Column18] [varchar](50) NULL,
    [Column19] [varchar](50) NULL,
    [Column20] [varchar](60) NULL,
    [Column21] [varchar](20) NULL,
    [Column22] [varchar](120) NULL,
    [Column23] [varchar](4) NULL,
    [Column24] [varchar](75) NULL,
    [Column25] [char](1) NULL,
    [Column26] [varchar](50) NULL,
    [Column27] [varchar](128) NULL,
    [Column28] [varchar](50) NULL,
    [Column29] [int] NULL,
    [Column30] [text] NULL,
 CONSTRAINT [PK] PRIMARY KEY CLUSTERED 
(
    [Column1] ASC,
    [Column2] ASC,
    [Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column4]  DEFAULT (0) FOR [Column4]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column5]  DEFAULT (0) FOR [Column5]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column15]  DEFAULT (0) FOR [Column15]
GO
ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column16]  DEFAULT (0) FOR [Column16]
GO

Ecco i risultati dell'esecuzione dei comandi nella risposta di Max Vernon:

╔════════════╦═══════════╦════════════╦═════════════════╦══════════════════════╦════════════════════╗
 TotalBytes  FreeBytes  TotalPages  TotalEmptyPages  PageBytesFreePercent  UnusedPagesPercent 
╠════════════╬═══════════╬════════════╬═════════════════╬══════════════════════╬════════════════════╣
  9014280192 8653594624     1100376          997178             95.998700           90.621500 
╚════════════╩═══════════╩════════════╩═════════════════╩══════════════════════╩════════════════════╝
╔═════════════╦═══════════════════╦════════════════════╗
 ObjectName   ReservedPageCount       UsedPageCount 
╠═════════════╬═══════════════════╬════════════════════╣
 dbo.MyTable            5109090             2850245 
╚═════════════╩═══════════════════╩════════════════════╝

AGGIORNARE:

Ho eseguito quanto segue come suggerito da Max Vernon:

DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');

Ed ecco l'output:

DBCC UPDATEUSAGE: Usage counts updated for table 'MyTable' (index 'PK_MyTable', partition 1):
        USED pages (LOB Data): changed from (568025) to (1019641) pages.
        RSVD pages (LOB Data): changed from (1019761) to (1019763) pages.

Ciò ha aggiornato l'utilizzo del disco per la tabella:

inserisci qui la descrizione dell'immagine

E l'utilizzo complessivo del disco:

inserisci qui la descrizione dell'immagine

Quindi, sembra che il problema sia che l'utilizzo del disco, come monitorato da SQL Server, è diventato completamente fuori sincrono rispetto all'utilizzo effettivo del disco. Considererò questo problema risolto, ma sarei interessato a sapere perché questo sarebbe successo in primo luogo!

Risposte:


10

Avrei eseguito DBCC UPDATEUSAGE contro la tabella come primo passo, poiché i sintomi mostrano un uso incoerente dello spazio.

DBCC UPDATEUSAGE corregge le righe, le pagine utilizzate, le pagine riservate, le pagine foglia e il conteggio delle pagine dati per ciascuna partizione in una tabella o indice. Se non vi sono inesattezze nelle tabelle di sistema, DBCC UPDATEUSAGE non restituisce dati. Se le imprecisioni vengono rilevate e corrette e WITH NO_INFOMSGS non viene utilizzato, DBCC UPDATEUSAGE restituisce le righe e le colonne che vengono aggiornate nelle tabelle di sistema.

La sintassi è:

DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');

Dopo averlo eseguito, avrei corso EXEC sys.sp_spaceusedcontro il tavolo:

EXEC sys.sp_spaceused @objname = N'dbo.MyTable'
    , @updateusage = 'false' --true or false
    , @mode = 'ALL' --ALL, LOCAL_ONLY, REMOTE_ONLY
    , @oneresultset = 1;

Il comando sopra ha la possibilità di aggiornare l'utilizzo, ma dal momento che è stato eseguito DBCC UPDATEUSAGEprima manualmente, lasciare quello impostato su false. L'esecuzione DBCC UPDATEUSAGEmanuale ti consente di vedere se qualcosa è stato corretto.

La query seguente dovrebbe mostrare la percentuale di byte liberi nella tabella e la percentuale di pagine libere nella tabella. Poiché la query utilizza una funzionalità non documentata, non è saggio contare sui risultati, ma sembra accurato se confrontato con l'output da sys.sp_spaceused, a un livello elevato.

Se la percentuale di byte liberi è significativamente superiore alla percentuale di pagine libere, allora hai molte pagine parzialmente vuote.

Le pagine parzialmente vuote possono derivare da una serie di cause, tra cui:

  1. Divisioni di pagina, in cui la pagina deve essere suddivisa per accogliere nuovi inserti nell'indice cluster

  2. Impossibilità di riempire la pagina di colonne a causa delle dimensioni della colonna.

La query utilizza la sys.dm_db_database_page_allocationsfunzione di gestione dinamica non documentata :

;WITH dpa AS 
(
    SELECT dpa.*
        , page_free_space_percent_corrected = 
          CASE COALESCE(dpa.page_type_desc, N'')
            WHEN N'TEXT_MIX_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            WHEN N'TEXT_TREE_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            ELSE COALESCE(dpa.page_free_space_percent, 100)
          END
    FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.MyTable'), NULL, NULL, 'DETAILED') dpa
)
, src AS
(
SELECT TotalKB = COUNT_BIG(1) * 8192 / 1024
    , FreeKB = SUM((dpa.page_free_space_percent_corrected / 100) * CONVERT(bigint, 8192)) / 1024
    , TotalPages = COUNT_BIG(1)
    , TotalEmptyPages = SUM(CASE WHEN dpa.page_free_space_percent_corrected = 100 THEN 1 ELSE 0 END) --completely empty pages
FROM dpa
)
SELECT *
    , BytesFreePercent = (CONVERT(decimal(38,2), src.FreeKB) / src.TotalKB) * 100
    , UnusedPagesPercent = (CONVERT(decimal(38,2), src.TotalEmptyPages) / src.TotalPages) * 100
FROM src

L'output è simile a:

╔═════════╦════════╦════════════╦═════════════════ ╦══════════════════╦════════════════════╗
║ TotalKB ║ FreeKB ║ TotalPages ║ TotalEmptyPages ║ BytesFreePercent ║ UnusedPagesPercent ║
╠═════════╬════════╬════════════╬═════════════════ ╬══════════════════╬════════════════════╣
║ 208 ║ 96 ║ 26 ║ 12 ║ 46,153800 ║ 46,153800 ║
╚═════════╩════════╩════════════╩═════════════════ ╩══════════════════╩════════════════════╝

Ho scritto un post sul blog descrivendo la funzione qui .

Nel tuo scenario, dal momento che hai eseguito ALTER TABLE ... REBUILD, dovresti vedere un numero molto basso per TotalEmptyPages, ma suppongo che avrai ancora circa il 72% in BytesFreePercent.

Ho usato il tuo CREATE TABLEscript per tentare di ricreare il tuo scenario.

Questo è l' MCVE che sto usando:

DROP TABLE IF EXISTS dbo.MyTable;

CREATE TABLE [dbo].[MyTable](
    [Column1]  [int]            NOT NULL IDENTITY(1,1),
    [Column2]  [int]            NOT NULL,
    [Column3]  [int]            NOT NULL,
    [Column4]  [bit]            NOT NULL,
    [Column5]  [tinyint]        NOT NULL,
    [Column6]  [datetime]       NULL,
    [Column7]  [int]            NOT NULL,
    [Column8]  [varchar](100)   NULL,
    [Column9]  [varchar](256)   NULL,
    [Column10] [int]            NULL,
    [Column11] [image]          NULL,
    [Column12] [text]           NULL,
    [Column13] [varchar](100)   NULL,
    [Column14] [varchar](6)     NULL,
    [Column15] [int]            NOT NULL,
    [Column16] [bit]            NOT NULL,
    [Column17] [datetime]       NULL,
    [Column18] [varchar](50)    NULL,
    [Column19] [varchar](50)    NULL,
    [Column20] [varchar](60)    NULL,
    [Column21] [varchar](20)    NULL,
    [Column22] [varchar](120)   NULL,
    [Column23] [varchar](4)     NULL,
    [Column24] [varchar](75)    NULL,
    [Column25] [char](1)        NULL,
    [Column26] [varchar](50)    NULL,
    [Column27] [varchar](128)   NULL,
    [Column28] [varchar](50)    NULL,
    [Column29] [int]            NULL,
    [Column30] [text]           NULL,
 CONSTRAINT [PK] PRIMARY KEY CLUSTERED 
(
    [Column1] ASC,
    [Column2] ASC,
    [Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column4]  DEFAULT (0) FOR [Column4]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column5]  DEFAULT (0) FOR [Column5]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column15]  DEFAULT (0) FOR [Column15]

ALTER TABLE [dbo].[MyTable] ADD  CONSTRAINT [DF_Column16]  DEFAULT (0) FOR [Column16]
GO

INSERT INTO dbo.MyTable (
      Column2
    , Column3
    , Column4
    , Column5
    , Column6
    , Column7
    , Column8
    , Column9
    , Column10
    , Column11
    , Column12
    , Column13
    , Column14
    , Column15
    , Column16
    , Column17
    , Column18
    , Column19
    , Column20
    , Column21
    , Column22
    , Column23
    , Column24
    , Column25
    , Column26
    , Column27
    , Column28
    , Column29
    , Column30
)
VALUES (
          0
        , 0
        , 0
        , 0
        , '2019-07-09 00:00:00'
        , 1
        , REPLICATE('A', 50)    
        , REPLICATE('B', 128)   
        , 0
        , REPLICATE(CONVERT(varchar(max), 'a'), 1)
        , REPLICATE(CONVERT(varchar(max), 'b'), 9000)
        , REPLICATE('C', 50)    
        , REPLICATE('D', 3)     
        , 0
        , 0
        , '2019-07-10 00:00:00'
        , REPLICATE('E', 25)    
        , REPLICATE('F', 25)    
        , REPLICATE('G', 30)    
        , REPLICATE('H', 10)    
        , REPLICATE('I', 120)   
        , REPLICATE('J', 4)     
        , REPLICATE('K', 75)    
        , 'L'       
        , REPLICATE('M', 50)    
        , REPLICATE('N', 128)   
        , REPLICATE('O', 50)    
        , 0
        , REPLICATE(CONVERT(varchar(max), 'c'), 90000)
);
--GO 100

;WITH dpa AS 
(
    SELECT dpa.*
        , page_free_space_percent_corrected = 
          CASE COALESCE(dpa.page_type_desc, N'')
            WHEN N'TEXT_MIX_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            WHEN N'TEXT_TREE_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
            ELSE COALESCE(dpa.page_free_space_percent, 100)
          END
    FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.MyTable'), NULL, NULL, 'DETAILED') dpa
)
, src AS
(
SELECT TotalKB = COUNT_BIG(1) * 8192 / 1024
    , FreeKB = SUM((dpa.page_free_space_percent_corrected / 100) * CONVERT(bigint, 8192)) / 1024
    , TotalPages = COUNT_BIG(1)
    , TotalEmptyPages = SUM(CASE WHEN dpa.page_free_space_percent_corrected = 100 THEN 1 ELSE 0 END) --completely empty pages
FROM dpa
)
SELECT *
    , BytesFreePercent = (CONVERT(decimal(38,2), src.FreeKB) / src.TotalKB) * 100
    , UnusedPagesPercent = (CONVERT(decimal(38,2), src.TotalEmptyPages) / src.TotalPages) * 100
FROM src

La query seguente mostra una singola riga per ogni pagina allocata alla tabella e utilizza lo stesso DMV non documentato:

SELECT DatabaseName = d.name
    , ObjectName = o.name
    , IndexName = i.name
    , PartitionID = dpa.partition_id
    , dpa.allocation_unit_type_desc
    , dpa.allocated_page_file_id
    , dpa.allocated_page_page_id
    , dpa.is_allocated
    , dpa.page_free_space_percent --this seems unreliable
    , page_free_space_percent_corrected = 
        CASE COALESCE(dpa.page_type_desc, N'')
        WHEN N'TEXT_MIX_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
        WHEN N'TEXT_TREE_PAGE' THEN 100 - COALESCE(dpa.page_free_space_percent, 100)
        ELSE COALESCE(dpa.page_free_space_percent, 100)
        END
    , dpa.page_type_desc
    , dpa.is_page_compressed
    , dpa.has_ghost_records
FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.MyTable'), NULL, NULL, 'DETAILED') dpa
    LEFT JOIN sys.databases d ON dpa.database_id = d.database_id
    LEFT JOIN sys.objects o ON dpa.object_id = o.object_id
    LEFT JOIN sys.indexes i ON dpa.object_id = i.object_id AND dpa.index_id = i.index_id
WHERE dpa.database_id = DB_ID() --sanity check for sys.objects and sys.indexes

L'output mostrerà molte righe se lo esegui sulla tua vera tabella nel tuo ambiente di test, ma potrebbe permetterti di vedere dove si trova il problema.

Puoi eseguire il seguente script e pubblicare i risultati nella tua domanda? Sto solo cercando di assicurarmi che siamo sulla stessa pagina.

SELECT ObjectName = s.name + N'.' + o.name
    , ReservedPageCount = SUM(dps.reserved_page_count)
    , UsePageCount = SUM(dps.used_page_count)
FROM sys.schemas s
    INNER JOIN sys.objects o ON s.schema_id = o.schema_id
    INNER JOIN sys.partitions p ON o.object_id = p.object_id
    INNER JOIN sys.dm_db_partition_stats dps ON p.object_id = dps.object_id
WHERE s.name = N'dbo'
    AND o.name = N'MyTable'
GROUP BY s.name + N'.' + o.name;

2
L'esecuzione ha DBCC UPDATEUSAGEaggiornato lo spazio inutilizzato e il conteggio delle pagine inutilizzate. Sembra che l'utilizzo del disco e le informazioni sulla pagina segnalate da SQL Server siano estremamente non sincronizzate: ho aggiornato il mio post con i dettagli. Sono curioso di sapere come sarebbe potuto succedere in primo luogo, ma almeno il problema è stato riscontrato. Grazie per tutto il tuo aiuto, lo apprezzo davvero!
Ken,

0

Una delle colonne è un LOB di tipo image e contiene file di dimensioni variabili da pochi KB a diverse centinaia di MB

Potresti riscontrare una frammentazione interna.
Qual è la frammentazione della pagina per questa tabella?
E la frammentazione per la fila è diversa dalle pagine fuori fila?

Dici di avere file di pochi KB.
SQL Server memorizza tutto in pagine 8060 byte. Ciò significa che se si dispone di una riga (o di dati fuori riga) di 4040 byte e il successivo è simile, non è possibile inserirli entrambi nella stessa pagina e si sprecherà metà dello spazio. Prova a modificare le dimensioni della riga memorizzando colonne di lunghezza variabile (iniziare con l'immagine ad esempio) in una tabella diversa.


Non penso che la frammentazione sia il problema. Dopo aver ricostruito gli indici, la frammentazione per l'indice cluster è dello 0,45% e la sua pienezza di pagina è del 98,93%.
Ken,

La ricostruzione di una tabella o di un indice non sarà utile se si soffre di righe molto grandi o dati LOB che non si adattano bene alle pagine da 8 KB. È quello che Max Vernon ha spiegato in modo più dettagliato: "hai molte pagine parzialmente vuote". chiamato anche frammentazione interna
DrTrunks Bell

-3

Il database è in modalità di recupero completo? In tal caso, quando esegui una riduzione, registra tutte le modifiche e non le ridurrà nel modo previsto. A seconda delle ore di funzionamento, è possibile eseguire un backup, passare alla modalità di ripristino di invio in blocco e quindi eseguire la riduzione sul file di dati. Successivamente, si desidera eseguire gli script dell'indice per ripristinare / ricostruire e tornare al ripristino completo. Questo è ciò che proverei comunque, ma di nuovo dipende dalle tue ore di funzionamento per tutto questo.


4
Crescere il modello di recupero è interessante. Penso che sarebbe più applicabile se l'OP avesse problemi con le dimensioni del loro file di registro. Allo stato attuale, stanno avendo problemi con la dimensione del file di dati, quindi sarei sorpreso se il modello di recupero stava causando il problema descritto.
Josh Darnell,

È vero, ma le uniche volte in cui ho eseguito una contrazione e non ha avuto alcun impatto sullo spazio è stato a causa del modello di recupero, quindi ho pensato che valesse la pena sollevarlo in caso di diagnosi errata.
John-Henry Lochbaum,

-3

L'unica volta in cui non sono stato in grado di ridurre un DB e recuperare spazio è perché non è possibile ridurre un DB oltre la dimensione iniziale del DB quando è stato creato. Ad esempio, se il tuo DB è una copia del DB di produzione e hai creato il DB per la prima volta a 525 GB, il server sql non ti consentirà di ridurre le dimensioni al di sotto di 525 GB, indipendentemente dalla quantità di dati eliminati dal DB. Ma se il DB è stato creato sotto 383 GB e poi è cresciuto fino a 525 GB, non dovresti avere problemi a recuperare lo spazio. Ho pensato a lungo che questa è una restrizione stupida e arbitraria da parte di Microsoft.

Riduci il database solo fino alla dimensione iniziale impostata dopo la creazione del database


La domanda non riguarda il restringimento di un database (e se lo fosse, la capacità di ridurlo dipende dallo spazio utilizzato dopo l'area delle dimensioni iniziali)
eckes

Finché c'è spazio inutilizzato, è possibile ridurre il database a un paio di MB indipendentemente dalle dimensioni originali. Non è necessariamente una buona idea, ma ho avuto molte occasioni per ridurre i database e non ho mai incontrato un limite come questo.
Ray

-3

Ho riscontrato questo problema in precedenza su scatole di produzione, quello che devi fare è ricostruire tabelle e indici per ogni tabella (in quell'ordine).

Ecco la domanda che uso per tenere sotto controllo le tabelle. Ti aiuterà a determinare quali tabelle devono essere ricostruite e creare le query SQL che devi eseguire. Questa query è limitata a quelli con uno spazio inutilizzato superiore a 1 MB e un rapporto inutilizzato del 5%, in modo da ricostruire solo ciò su cui è realmente necessario concentrarsi:

SELECT  'alter table [' + t.NAME + '] rebuild;' AS SQL1, 'alter index all on [' + t.NAME + '] rebuild;' as SQL2, 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, case when SUM(a.total_pages)=0 then 0 else (SUM(a.total_pages) - SUM(a.used_pages))*100/SUM(a.total_pages) end as Ratio  FROM     sys.tables t (nolock) INNER JOIN       sys.indexes i (nolock)  ON t.OBJECT_ID = i.object_id INNER JOIN  sys.partitions p (nolock) ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id INNER JOIN  sys.allocation_units a (nolock) ON p.partition_id = a.container_id LEFT OUTER JOIN  sys.schemas s (nolock) ON t.schema_id = s.schema_id WHERE  t.is_ms_shipped = 0 AND i.OBJECT_ID > 255  GROUP BY  t.Name, s.Name, p.Rows  
having  (SUM(a.total_pages) - SUM(a.used_pages)) * 8/1024>1
and (SUM(a.total_pages) - SUM(a.used_pages))*100/SUM(a.total_pages)>5
ORDER BY    5 desc

ricostruire la tabella, come afferma il PO, avrebbe eliminato la maggior parte della frammentazione. Dubito che fare un'altra ricostruzione aiuterà ulteriormente.
Max Vernon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.