2014-11-24 26 views
1

我有一張擁有超過1.5億條記錄的表。目前的類型如下:在非常大的表上縮小數據類型

id (Primary Key, Bigint) 
idResult (Foreign key, Bigint null) 
idItem (Foreign Key, Bigint null) 
Number_1 (Bigint null) 
Number_2 (Bigint null) 
IsActive (Bigint null) 

Number_1和Number_2永遠不會大於10. IsActive明顯是布爾值。並且這些列中的任何一個都不能在代碼庫的其他地方空着。我還想將外鍵字段更改爲int,但這是另一回事。這張桌子是在我開始幾年前建成的,我們正在經歷一些成長的痛苦。

我正在尋找轉換這些列(和其他表上的其他幾個人,儘管這是主要罪犯)的最佳方式,並回收該磁盤空間。我嘗試過直線Alter Table,但是,有些預計,這只是無效。我不記得具體的錯誤,但我相信這與相關表格的大小有關。

現在我正在考慮手動刪除和重新創建表格,但我正在努力找出一個更好的方法來做到這一點,除了使用Select TOP 10000 * FROM dbo.TABLENAME WHERE id > 0,並且只是多次增加where子句。

我已經看過Switch To,但這需要目標表的數據類型與源表匹配,這正是我試圖解決的問題!

有什麼建議嗎?我看着這個錯誤的方式嗎?

+0

該錯誤消息將是有益的看到的。原則上沒有什麼會阻止這種操作。也許日誌已滿。您是否考慮過ROW和PAGE壓縮?我相信他們可以縮小數據類型的大小。 – usr 2014-11-24 21:52:53

+0

它給了我一個不同的錯誤之前,但現在我所得到的是一個超時。 >。無論如何,根據http://aboutsqlserver.com/2010/09/01/hidden-facts-about-table-alteration/這聽起來像做一個改變不會實際上減少磁盤上的行大小。 – anick 2014-11-24 22:02:32

+0

重建確實釋放了空間。關閉超時並繼續。 – usr 2014-11-24 22:08:31

回答

1

首先,謝謝你這樣做。這是一個明顯的勝利,許多人看不到太多的價值,但它將是非常值得的:)。讓世界變得如此輕鬆一點。

關於IsActive是一個布爾值。我的猜測是您正在考慮將其設爲BIT字段。這可能是要走的路,但有時最好與TINYINT一起去,因爲有可能將意義擴展到2個以上的州。在這種情況下,它真的變得更多的是StatusID。通常情況下,這是一個簡單的開始活動/不活動,但稍後可能刪除和/或其他人。從大小的角度來看,TINYINT總是1個字節。另一方面,BIT爲1字節最多8 BIT字段。這意味着,一個BIT字段是1個字節,2個BIT字段也是一個字節,所以最多有8個BIT字段存儲在一個字節中。因此,當表格只有1 BIT字段時,選擇BIT而不是TINYINT節省空間。只是需要考慮。

正如你所看到的那樣,ALTER TABLE對於大型表格來說有點多。一種選擇,雖然不是很好,但增加一個NOT NULL字段 - Number_1new - 具有DEFAULT的值(由於默認值,這至少從SQL 2012開始,這將是即時的),它們都不會自然具有(例如,255),然後慢慢遷移值,在一個循環中,如:

UPDATE TOP (5000) tab 
SET tab.Number_1new = tab.Number_1 
FROM [table] tab 
WHERE tab.Number_1new = 255; 

當做到這一點然後做:

sp_rename 'table.Number_1', 'Number_1old', 'COLUMN'; 
sp_rename 'table.Number_1new', 'Number_1', 'COLUMN'; 

當然,最好能包住,在一個事務中,幷包裹在TRY/CATCH中。當相關代碼已更新並且所有內容都經過測試並且數據看起來不錯時,則可以刪除Number_1old列。

但是,我發現的最佳方式是創建一個新表,緩慢地轉換數據,然後同時交換表和代碼。我詳細介紹了SQL Server Central文章中的步驟:Restructure 100 Million Row (or more) Tables in Seconds. SRSLY!(需要免費註冊)。以防萬一遇到那篇文章有問題,下面是基本步驟:

  1. 創建一個具有理想結構的新表格 - [tableNew]。如果您使用的是Enterprise Edition,請考慮啓用ROW或PAGE壓縮,因爲它們有時可以提供幫助。但是請先做一些研究,因爲有些情況會有負面影響。有MSDN上的文檔可以幫助你理清它以及一些工具來幫助估計潛在的節省。但即使你確實啓用了壓縮,我也不會將這個動作視爲替換你在這裏執行的項目。
  2. 在[table]上添加一個觸發器AFTER UPDATE, DELETE以保持更改同步(但不需要擔心新行)
  3. 創建一個SQL代理作業,可以批量移動缺少的行。做一個循環,做一個INSERT INTO [tableNew] (Columns) SELECT TOP (n) Columns FROM [table] WHERE ?? ORDER BY ??
  4. WHERE和ORDER BY子句取決於情況。他們應該着眼於充分利用聚集索引。如果新表的聚集索引在結構上與舊/當前表相同,則在每個循環的開始處,您可以從[tableNew]中獲取MAX([id])並使用它獲取WHERE table.[id] > @MaxIdInTableNew ORDER BY table.[id]
  5. 創建新表,在當前表和SQL代理作業上觸發一週左右,然後再進行完全切換。這個時間框架可能會根據你的情況而改變,但是一定要給自己足夠的時間。對於這個工作來說,完成遷移行數要好得多,並且每次只有少量的滴答聲,而不是因爲發佈應該開始的時候是100k害羞的全集。
  6. 如果計劃要遷移其他相關表(這兩個FK的PK引用要變成INT s),那麼現在在這裏將這些字段設置爲INT,並且不添加FK直到其他表被遷移到具有INT字段作爲其PK。您不希望再次重建此表,只是爲了對FK字段進行更改。
  7. 在割接(在一個try/catch,當然):
    1. BEGIN TRAN
    2. 做最後的行數這兩個表,以確保一切移過(可能要理智檢查發佈前行,以確保觸發做了更新和刪除如預期)
    3. 命名當前表到「老」
    4. 重命名「新」表中沒有「新」
    5. 降SQL代理作業(或至少禁用它)
    6. 重命名和相關對象,如約束等
    7. COMMIT
1

不要跌落

適當列類型創建一個表,然後在一個循環

插入或者你可以在同一時間做一列。

Update table 
set Number_1tiny = Number_1 
where Number_1 is not null 
and Number_1tiny <> Number_1