2010-05-03 21 views
41

Estoy intentando escribir un script de actualización de base de datos de SQL Server. Quiero probar la existencia de una columna en una tabla, luego, si no existe, agregue la columna con un valor predeterminado y finalmente actualice esa columna según el valor actual de una columna diferente en la misma tabla. Quiero que este script sea ejecutable varias veces, la primera vez que actualice la tabla y en las ejecuciones subsiguientes se ignore el script. Mi script actualmente tiene el siguiente aspecto:Columna de prueba existe, Agregar columna y Columna de actualización

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable') 
BEGIN 

ALTER TABLE [dbo].[PurchaseOrder] ADD [IsDownloadable] bit NOT NULL DEFAULT 0 

UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref] IS NOT NULL 

END 

SQL Server devuelve error "nombre de columna no válido 'IsDownloadable'", es decir que necesito para cometer el DDL antes de que pueda actualizar la columna. He intentado varias permutaciones, pero no estoy llegando a ninguna parte rápidamente.

Respuesta

75

Este script no se ejecutará correctamente a menos que la columna ya existe, que es exactamente cuando no lo necesita.

Los scripts SQL deben analizarse antes de que se puedan ejecutar. Si la columna no existe en el momento en que se analiza el script, entonces el análisis fracasará. No importa que sus scripts creen la columna más adelante; el analizador no tiene forma de saber eso.

Debe ingresar una declaración GO (separador de lotes) si desea acceder a la columna que acaba de agregar. Sin embargo, una vez que haces eso, ya no puedes mantener ningún flujo de control o variables del lote anterior, es como ejecutar dos scripts separados. Esto hace que sea difícil hacer tanto DDL como DML, condicionalmente, al mismo tiempo.

La solución más simple, lo que probablemente me recomiendan para usted porque su LMD no es muy complejo, es el uso de SQL dinámico, que el analizador no intentará analizar hasta el "tiempo de ejecución":

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable') 
BEGIN 

    ALTER TABLE [dbo].[PurchaseOrder] ADD 
     [IsDownloadable] bit NOT NULL DEFAULT 0 

    EXEC sp_executesql 
     N'UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref] IS NOT NULL' 

END 
+0

Eso lo hizo, gracias. –

+3

explicación completa, gracias – Vladimirs

+1

Exactamente lo que estaba buscando también. Gracias por esto. –

-1

Intente agregar una instrucción "IR" después de ALTER TABLE.

Fue una novedad para mí, pero dice here que todas las declaraciones en un lote (las que preceden al GO) se compilan en un plan de consulta). Sin GO en el SQL, todo el plan es efectivamente una consulta.

EDIT: Desde GO da un error de sintaxis (que me pareció extraño), he creado algo similar, y se encontró que funcionaba

declare @doUpdate bit; 

SELECT @doUpdate = 0; 

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable') 
BEGIN 
SELECT @doUpdate=1 
END 

IF @doUpdate<>0 
    ALTER TABLE [dbo].[PurchaseOrder] ADD [IsDownloadable] bit NOT NULL DEFAULT 0 

IF @doUpdate<>0 
    UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref]=0 

COMMIT TRAN 
+0

Sintaxis incorrecta cerca de '0'. Esta es una de las permutaciones que ya he probado. –

1

A menudo me he molestado por este problema y, desafortunadamente, la solución sugerida en Aaronaught's answer se convierte rápidamente en un problema cuando se trata de @parameters y 'strings'. Sin embargo, he encontrado una solución alternativa al explotar el uso de sinónimos:

IF(COL_LENGTH('MyTable', 'NewCol') IS NULL) 
BEGIN 
    ALTER TABLE MyTable ADD NewCol VARCHAR(16) NULL; 

    CREATE SYNONYM hack FOR MyTable; 
    UPDATE hack SET NewCol = 'Hello ' + OldCol; 
    DROP SYNONYM hack; 

    ALTER TABLE MyTable ALTER COLUMN NewCol VARCHAR(16) NOT NULL; 
END 
Cuestiones relacionadas