Sql-Server

將數據移動到存檔表中並減小原始表的大小

  • February 27, 2017

在我繼承的 SQL Server 上,70GB 驅動器上有一個 55GB 的數據庫。有很多小表,還有一個 50GB 大小的大表(大約 36GB 數據,15GB 索引)。日誌文件位於不同的驅動器上。之後shrinkfile日誌文件的驅動器有大約 12GB 空閒空間。數據庫處於簡單恢復模式。

該表設計得很糟糕,例如有許多列定義為char(10)其中不超過 3 個字元。

驅動器上沒有足夠的可用空間來更改這些列,然後重建聚集索引(更改列時,數據庫會創建一個新列,然後從舊列複製數據)。

我想知道是否可以改為執行以下操作:

  • 創建具有更好的空間節省設計的存檔表(為它們包含的數據提供適當大小的列)
  • 將舊數據從大表增量移動到存檔表。

當從源表中刪除數據(移動到存檔表之後)時,源表的大小會減小嗎?或者我是否必須進行聚集索引重建才能發生這種情況?(在這種情況下我不能這樣做,因為沒有足夠的空間。)

我現在想到列更改會佔用日誌文件中的空間,但是每當我在較小的表上進行列更改時,主數據文件也會增長。

如果分配空間或其他驅動器是不可能的,那麼可能有一些方法可以讓您以最小的增長做到這一點,假設您可以將數據庫置於RESTRICTED_USER模式或以其他方式阻止使用者/應用程序在修復數據時嘗試修改數據。

首先,此時不要縮小數據或日誌文件。您將暫時釋放磁碟上的空間,但文件將再次增長 - 因此與大多數收縮操作一樣,這是沒有意義的。

接下來,取其中一個 char(10) 列。確保您知道包含的最大字元串:

SELECT MAX(DATALENGTH(CONVERT(varchar(10), char10_column))) 
FROM dbo.table;

添加一個可以為空的新varchar(that length)列。(我已經有一段時間不支持 2005 了,所以我忘記了這是完全線上還是部分線上 - 理論上,這不應該是數據大小的操作,但我不知道 2005 是否足夠聰明不要把它當作一個。)

ALTER TABLE dbo.table ADD newvarchar10_column varchar(10);

在小塊中,正如我在此處為刪除所描述的那樣,將新列更新為具有舊列的值,並將舊列設置為NULL. 這將逐漸增加表大小,但對日誌的影響很小。

UPDATE TOP (?top?) dbo.table -- not sure what your ?top? sweet spot will be
 SET newvarchar10_column = oldchar10_column,
     oldchar10_column = NULL
 WHERE oldchar10_column IS NOT NULL;

-- CHECKPOINT / BACKUP LOG

一旦數據全部位於新列中,刪除舊列對數據庫來說應該是最少的工作,因為沒有任何實際數據要記錄。如果此列涉及任何索引或約束,或模式綁定的視圖/函式,您必須首先處理這些。

ALTER TABLE dbo.table DROP COLUMN oldchar10_column;

然後重命名新列,使其看起來像舊列。

EXEC sys.sp_rename N'dbo.table.newvarchar10_column', 
      N'oldchar10_column', N'COLUMN';

不要忘記更新仍然認為此列是char(10).

這只是一個理論。您可能希望首先在非關鍵系統上對其進行測試,以測試對數據/日誌文件的影響,找出最佳位置?top?等。

將列更改為較小的大小應該不會有問題。無論如何,您只能使用 ALTER TABLE 語句一次更改一種列類型。雖然總數據文件大小會間歇性地增長,但它不能比使用更正列類型的原始文件大。

所以這是我要採取的步驟:

1)確保表上有一個主鍵,或者至少有一個聚集索引。

  1. 一次執行一個 ALTER TABLE ALTER COLUMN 語句

  2. 如果數據庫空間不足,請在適當的文件上執行 DBCC SHRINKFILE。

  3. 繼續第 2 步和第 3 步,直到所有列都是新的適當類型

5)重建數據庫上的所有索引,以消除前面步驟造成的碎片。

很容易。我還創建了一個具有正確定義的新表,將整個現有表複製到新表,刪除原始表並將新表重命名為原始表。但是,要做到這一點,至少間歇性地需要更多空間。您可以嘗試將其中一個 SQL Server 數據文件臨時放在網路驅動器上,但我認為只更改上述現有列會更容易。

引用自:https://dba.stackexchange.com/questions/163878