Cómo ALTERAR varias columnas a la vez en SQL Server

143

Necesito ALTERlos tipos de datos de varias columnas en una tabla.

Para una sola columna, lo siguiente funciona bien:

ALTER TABLE tblcommodityOHLC
ALTER COLUMN
    CC_CommodityContractID NUMERIC(18,0) 

Pero, ¿cómo modifico varias columnas en una declaración? Lo siguiente no funciona:

ALTER TABLE tblcommodityOHLC
ALTER COLUMN
    CC_CommodityContractID NUMERIC(18,0), 
    CM_CommodityID NUMERIC(18,0)
D.mahesh
fuente
1
¿Cuál es la ventaja percibida de hacerlo de una vez?
cuando el
55
@onedaywhen: para que SQL Server solo haga una pasada a través de la tabla para hacer cualquier validación necesaria contra el nuevo tipo de datos y / o escribir las columnas alteradas en el nuevo formato.
Martin Smith
1
No hay gran ventaja entonces!
cuando el
44
Contrario. Sería una gran ventaja tener una ejecución alternativa en 2 horas en lugar de 24 para columnas múltiples en tablas grandes.
Norbert Kardos

Respuestas:

135

Esto no es posible. Tendrá que hacer esto uno por uno.

Puede crear una tabla temporal con sus columnas modificadas, copiar los datos, soltar su tabla original y cambiar el nombre de su tabla temporal a su nombre original.

Neil Knight
fuente
55
+1, You will need to do this one by one.entonces, ¿cuál es el problema, solo use múltiples ALTER TABLE ALTER COLUMNcomandos?
KM.
66
@KM Un problema es si está alterando una tabla grande. Cada declaración significa una nueva exploración, pero si se puede alterar varias columnas, toda alteración podría haber sido mucho más rápido
erikkallen
@erikkallen, luego, al igual que las herramientas SSMS, generalmente generan sus scripts: crean una nueva tabla y replican FK e índices, etc., sueltan la tabla original y luego cambian el nombre de la nueva tabla,
KM.
Dejar y recrear tablas es una operación bastante intensiva. Está deshabilitado por defecto en SSMS ahora, y probablemente por una buena razón.
Jocull
"Each statement means a new scan": No necesariamente @erikkallen. En algunos casos, la columna ALTER es un simple cambio de metadatos. Le invitamos a unirse a mi conferencia mañana sobre " SQL Internals - Physical Table Structure under the hood, and implementation on real case scenarios" para ver todo en la práctica :-)
Ronen Ariely
26

No es posible realizar múltiples ALTER COLUMNacciones dentro de una sola ALTER TABLEdeclaración.

Vea la ALTER TABLEsintaxis aquí

Puedes hacer múltiples ADDo múltiples DROP COLUMN, pero solo uno ALTER COLUMN.

Fernando Torres
fuente
17

Como han respondido otros, necesita múltiples ALTER TABLEdeclaraciones.
Intenta lo siguiente:

ALTER TABLE tblcommodityOHLC alter column CC_CommodityContractID NUMERIC(18,0);
ALTER TABLE tblcommodityOHLC alter column CM_CommodityID NUMERIC(18,0);
Ray K.
fuente
12

La siguiente solución no es una declaración única para alterar varias columnas, pero sí, simplifica la vida:

  1. Generar un CREATEguión de tabla .

  2. Reemplazar CREATE TABLEcon ALTER TABLE [TableName] ALTER COLUMNpara primera línea de

  3. Eliminar columnas no deseadas de la lista.

  4. Cambie los tipos de datos de columnas como desee.

  5. Realice una búsqueda y reemplazo ... de la siguiente manera:

    1. Encontrar: NULL,
    2. Reemplazar con: NULL; ALTER TABLE [TableName] ALTER COLUMN
    3. Hit Reemplazar botón.
  6. Ejecute el script

Espero que ahorre mucho tiempo :))

Pritam
fuente
6

Como muchos otros han dicho, necesitará usar varias ALTER COLUMNdeclaraciones, una para cada columna que desee modificar.

Si desea modificar todas o varias de las columnas de su tabla al mismo tipo de datos (como expandir un campo VARCHAR de 50 a 100 caracteres), puede generar todas las declaraciones automáticamente utilizando la consulta a continuación. Esta técnica también es útil si desea reemplazar el mismo carácter en varios campos (como eliminar \ t de todas las columnas).

SELECT
     TABLE_CATALOG
    ,TABLE_SCHEMA
    ,TABLE_NAME
    ,COLUMN_NAME
    ,'ALTER TABLE ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] ALTER COLUMN ['+COLUMN_NAME+'] VARCHAR(300)' as 'code'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'your_table' AND TABLE_SCHEMA = 'your_schema'

Esto genera una ALTER TABLEdeclaración para cada columna para usted.

Evan
fuente
1

Si realiza los cambios en el estudio de gestión y genera scripts, crea una nueva tabla e inserta los datos antiguos con los tipos de datos modificados. Aquí hay un pequeño ejemplo que cambia los tipos de datos de dos columnas.

/*
   12 August 201008:30:39
   User: 
   Server: CLPPRGRTEL01\TELSQLEXPRESS
   Database: Tracker_3
   Application: 
*/

/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.tblDiary
    DROP CONSTRAINT FK_tblDiary_tblDiary_events
GO
ALTER TABLE dbo.tblDiary_events SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_tblDiary
    (
    Diary_ID int NOT NULL IDENTITY (1, 1),
    Date date NOT NULL,
    Diary_event_type_ID int NOT NULL,
    Notes varchar(MAX) NULL,
    Expected_call_volumes real NULL,
    Expected_duration real NULL,
    Skill_affected smallint NULL
    )  ON T3_Data_2
     TEXTIMAGE_ON T3_Data_2
GO
ALTER TABLE dbo.Tmp_tblDiary SET (LOCK_ESCALATION = TABLE)
GO
SET IDENTITY_INSERT dbo.Tmp_tblDiary ON
GO
IF EXISTS(SELECT * FROM dbo.tblDiary)
     EXEC('INSERT INTO dbo.Tmp_tblDiary (Diary_ID, Date, Diary_event_type_ID, Notes, Expected_call_volumes, Expected_duration, Skill_affected)
        SELECT Diary_ID, Date, Diary_event_type_ID, CONVERT(varchar(MAX), Notes), Expected_call_volumes, Expected_duration, CONVERT(smallint, Skill_affected) FROM dbo.tblDiary WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_tblDiary OFF
GO
DROP TABLE dbo.tblDiary
GO
EXECUTE sp_rename N'dbo.Tmp_tblDiary', N'tblDiary', 'OBJECT' 
GO
ALTER TABLE dbo.tblDiary ADD CONSTRAINT
    PK_tblDiary PRIMARY KEY NONCLUSTERED 
    (
    Diary_ID
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 86, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON T3_Data_2

GO
CREATE UNIQUE CLUSTERED INDEX tblDiary_ID ON dbo.tblDiary
    (
    Diary_ID
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 86, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON T3_Data_2
GO
CREATE NONCLUSTERED INDEX tblDiary_date ON dbo.tblDiary
    (
    Date
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 86, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON T3_Data_2
GO
ALTER TABLE dbo.tblDiary WITH NOCHECK ADD CONSTRAINT
    FK_tblDiary_tblDiary_events FOREIGN KEY
    (
    Diary_event_type_ID
    ) REFERENCES dbo.tblDiary_events
    (
    Diary_event_ID
    ) ON UPDATE  CASCADE 
     ON DELETE  CASCADE 

GO
COMMIT
Kevin Ross
fuente
1

Si no desea escribir todo usted mismo y cambiar todas las columnas al mismo tipo de datos, esto puede facilitarlo:

select 'alter table tblcommodityOHLC alter column '+name+ 'NUMERIC(18,0);'
from syscolumns where id = object_id('tblcommodityOHLC ')

Puede copiar y pegar el resultado como su consulta

Rishi
fuente
0
select 'ALTER TABLE ' + OBJECT_NAME(o.object_id) + 
    ' ALTER COLUMN ' + c.name + ' DATETIME2 ' + 
    CASE WHEN c.is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
from sys.objects o
inner join sys.columns c on o.object_id = c.object_id
inner join sys.types t on c.system_type_id = t.system_type_id
where o.type='U'
and c.name = 'Timestamp'
and t.name = 'datetime'
order by OBJECT_NAME(o.object_id)

cortesía de devio

Spyder
fuente
0

Gracias al ejemplo de código de Evan, pude modificarlo más y hacerlo más específico para las tablas que comienzan con nombres de columnas específicos Y manejar detalles específicos para las restricciones también. Ejecuté ese código y luego copié la columna [CÓDIGO] y lo ejecuté sin problemas.

USE [Table_Name]
GO
SELECT
     TABLE_CATALOG
    ,TABLE_SCHEMA
    ,TABLE_NAME
    ,COLUMN_NAME
    ,DATA_TYPE
    ,'ALTER TABLE ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] DROP CONSTRAINT [DEFAULT_'+TABLE_NAME+'_'+COLUMN_NAME+']; 
ALTER TABLE  ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] ALTER COLUMN ['+COLUMN_NAME+'] datetime2 (7) NOT NULL 
ALTER TABLE  ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] ADD CONSTRAINT [DEFAULT_'+TABLE_NAME+'_'+COLUMN_NAME+'] DEFAULT (''3/6/2018 6:47:23 PM'') FOR ['+COLUMN_NAME+']; 
GO' AS '[CODE]'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE 'form_%' AND TABLE_SCHEMA = 'dbo'
 AND (COLUMN_NAME = 'FormInserted' OR COLUMN_NAME = 'FormUpdated')
 AND DATA_TYPE = 'datetime'
Brenden Kehren
fuente
0
-- create temp table 
CREATE TABLE temp_table_alter
(
column_name varchar(255)    
);

-- insert those coulmns in temp table for which we nee to alter size of columns 
INSERT INTO temp_table_alter (column_name) VALUES ('colm1');
INSERT INTO temp_table_alter (column_name) VALUES ('colm2');
INSERT INTO temp_table_alter (column_name) VALUES ('colm3');
INSERT INTO temp_table_alter (column_name) VALUES ('colm4');

DECLARE @col_name_var varchar(255);
DECLARE alter_table_cursor CURSOR FOR
select column_name from temp_table_alter ;

OPEN alter_table_cursor
FETCH NEXT FROM alter_table_cursor INTO @col_name_var
WHILE @@FETCH_STATUS = 0
 BEGIN

 PRINT('ALTER COLUMN ' + @col_name_var);
 EXEC ('ALTER TABLE Original-table  ALTER COLUMN ['+ @col_name_var + '] DECIMAL(11,2);')

 FETCH NEXT FROM alter_table_cursor INTO @col_name_var
 END

CLOSE alter_table_cursor
DEALLOCATE alter_table_cursor

-- at the end drop temp table
drop table temp_table_alter;
Aurangzeb khan
fuente
NO es una buena solución. ¡Los cursores y bucles deben evitarse a TODA COSTA!
Ray
1
No es cierto, Ray. Los cursores y los bucles están bien para ciertas tareas DDL y otras tareas necesariamente RBR.
Jeff Moden
-3

Podemos alterar múltiples columnas en una sola consulta como esta:

ALTER TABLE `tblcommodityOHLC`
    CHANGE COLUMN `updated_on` `updated_on` DATETIME NULL DEFAULT NULL AFTER `updated_by`,
    CHANGE COLUMN `delivery_datetime` `delivery_datetime` DATETIME NULL DEFAULT CURRENT_TIMESTAMP AFTER `delivery_status`;

Simplemente dé las consultas como separadas por comas.

Muhammed Shihabudeen Labba A
fuente
44
Creo que esto es MySql? La pregunta era para SQL Server, y esto no funciona en el servidor SQL
Tjipke
-4

Ponga la ALTER COLUMNdeclaración dentro de un paréntesis, debería funcionar.

ALTER TABLE tblcommodityOHLC alter ( column  
CC_CommodityContractID NUMERIC(18,0), 
CM_CommodityID NUMERIC(18,0) )
Jai Prakash Rai
fuente
-4

Si entendí su pregunta correctamente, puede agregar varias columnas en una tabla utilizando la consulta mencionada a continuación.

Consulta:

Alter table tablename add (column1 dataype, column2 datatype);
Virendra Gawade
fuente
44
El OP preguntó sobre la columna ALTER, no ADD.
DR