2011-11-08 4 views
2

J'ai une base de données SQL Server 2008. Cette base de données a une colonne qui représente un "bit". Ce drapeau doit maintenant être un int. Mon problème est, il y a déjà beaucoup de données dans le tableau. Y a-t-il un moyen de faire facilement cette conversion? Considérant que j'utilise un type de données plus grand, je ne vois pas le problème. Cependant, le problème est le contenu des données. Comment mapper les champs de bits aux valeurs int correspondantes tout en changeant le type de colonne.SQL Server + Modification d'un type de colonne

Merci,

Répondre

1

Je un long article sur ce sujet au SQL Server table columns under the hood. Faites comme SQLMenace suggère, en utilisant ALTER TABLE ... ALTER COLUMN. Étant donné que cette modification augmente la taille de la colonne, la modification ne peut pas être uniquement des métadonnées. Elle doit effectuer une opération de taille de données (mise à jour à chaque ligne). Mais cela sera transparent pour vous (lisez l'article lié pour plus de détails). Cependant, vous devez être prêt à gérer une transaction potentiellement importante à mesure que la mise à jour de chaque ligne progresse, alors assurez-vous d'avoir suffisamment de journaux pour ne pas manquer d'espace de journalisation en raison de la transaction importante.

2

SQLMenace a la bonne réponse.

Si la table est simplement énorme, le changement pourrait prendre un certain temps et pourrait bloquer pendant une longue période. Si oui, et votre base de données ne peut pas tolérer longtemps vers le bas pour cette table (? Peut-être est OLTP 24 heures), ma suggestion serait de faire quelque chose comme ceci:

--Add a new temporary column to store the changed value. 
ALTER TABLE dbo.TableName ADD NewColumnName int NULL; 
CREATE NONCLUSTERED INDEX IX_TableName_NewColumnName ON dbo.TableName (NewColumName) 
    INCLUDE (ColumnName); -- the include only works on SQL 2008 and up 
-- This index may help or hurt performance, I'm not sure... :) 

-- Update the table in batches of 10000 at a time 
WHILE 1 = 1 BEGIN 
    UPDATE X -- Updating a derived table only works on SQL 2005 and up 
    SET X.NewColumnName = ColumnName 
    FROM (
     SELECT TOP 10000 * FROM dbo.TableName WHERE NewColumnName IS NULL 
    ) X; 
    IF @@RowCount = 0 BREAK; 
END; 

ALTER TABLE dbo.TableName ALTER COLUMN NewColumnName int NOT NULL; 

BEGIN TRAN; -- now do as *little* work as possible in this blocking transaction 
UPDATE T -- catch any updates that happened after we touched the row 
SET T.NewColumnName = T.ColumnName 
FROM dbo.TableName T WITH (TABLOCKX, HOLDLOCK) 
WHERE T.NewColumnName <> T.ColumnName; 
-- The lock hints ensure everyone is blocked until we do the switcheroo 

EXEC sp_rename 'TableName.ColumName', 'OldColumName'; 
EXEC sp_rename 'TableName.NewColumnName', 'ColumName'; 
COMMIT TRAN; 

DROP INDEX dbo.TableName.IX_TableName_NewColumnName;  
ALTER TABLE dbo.TableName DROP COLUMN OldColumnName; 

Mon script est non testé .. Cela pourrait être une bonne idée de le tester en premier. La mise à jour par lots réduit la taille de la transaction, empêche l'utilisation de la tempdb, la croissance du journal des tran et les longs verrous (la table peut être utilisée par d'autres clients entre chaque boucle de mise à jour). 10k est souvent une bonne taille, mais parfois des nombres plus petits peuvent être nécessaires en fonction du temps qu'il faut. Idéalement, vous choisiriez une taille qui utiliserait une bonne partie de la mémoire, mais n'écarterait personne et n'utiliserait que peu ou pas de tempdb. J'ai coupé des mises à jour multi-heures contre des tables énormes à quelques minutes (et plus important, non-bloquant minutes) avec ce genre de stratégie de bouclage. Remarque: pour ceux qui expérimentent les performances de cette stratégie, l'index non cluster que j'ai suggéré aidera plus lors de la partie transaction du script. Pour une simple énorme table, un index non cluster différent pourrait aider:

CREATE NONCLUSTERED INDEX IX_TableName_NewColumnName_Null ON dbo.TableName (NewColumName) 
WHERE NewColumnName IS NULL; -- SQL 2008 and up only 

D'autre part, en ajoutant l'indice nonclustered d'origine pourrait prendre plus de temps si elle est effectuée après la mise à jour des nouvelles données de colonne, ou potentiellement bloquer. Ou cela peut ne pas prendre beaucoup de temps. L'expérimentation est en ordre.