2010-03-02 607 views
6

Tengo un código similar al siguiente en un procedimiento almacenado que inserta una fila en una tabla, me gustaría establecer la última columna (CampoD) a @prmSomeValue a menos que sea nulo, de lo contrario simplemente use el valor predeterminado definido para esa columna.¿Hay alguna manera de utilizar de forma condicional los valores de columna predeterminados en una instrucción INSERT..SELECT?

IF (@prmSomeValue IS NULL) 
    INSERT INTO MyTable (fieldA,FieldB,FieldC) 
     SELECT A,B,C 
     FROM MyOtherTable 
ELSE 
    INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD) 
     SELECT A,B,C,@prmSomeValue 
     FROM MyOtherTable 

Esto funciona, pero infringe el principio DRY. Estoy tratando de encontrar la forma de hacer esto con una única instrucción de inserción. Algo similar al siguiente pseudocódigo.

INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD) 
     SELECT A,B,C,ISNULL(@prmSomeValue,DEFAULT) 
     FROM MyOtherTable 

¿Alguien tiene alguna idea?

Actualización - Una mayor restricción de giro
El defecto no es un valor literal, sino una función que se muestra a continuación.

...DEFAULT (suser_sname()) FOR [FieldD] 

actualización
finalmente despejaron y elegimos el menor de los males y acaba de copiar la función de valor por defecto en mi consulta en lugar de caer hasta el valor estándar configurado para la columna. No me gusta, pero hace el trabajo con menos repetición en mi consulta.

INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD) 
     SELECT A,B,C,ISNULL(@prmSomeValue,suser_sname()) 
     FROM MyOtherTable 

Respuesta

3

Desde esencialmente esto es lo que SQL Server está haciendo, usted podría hacer algo como esto para evitar por lo menos dos declaraciones casi idénticas (pseudo-código):

INSERT (columnA,B,C) ... ; 

IF @prmSomeValue IS NOT NULL 
    UPDATE ... ; 

No creo que haya una manera de COALESCE con el valor predeterminado.

+0

Interesante idea. No estoy seguro de si es preferible, pero definitivamente vale la pena votar. – JohnFx

+0

Gracias a JohnFx, creo que podría ser preferible una vez que haya más de una columna opcional que deba tratarse de esta manera. Entonces puedes decir "SI @foo NO ES NULO O @bar NO ES NULO, ACTUALIZA SET foo = COALESCE (@foo, foo), bar = COALESCE (@bar, bar) WHERE" etc., en lugar de escribir una instrucción de inserción para cada combinación posible. –

+0

Use ISNULL en lugar de COALESCE, COALESCE está pensado para múltiples argumentos. No me decantaría por los enfoques campo = parámetro/campo, mata el rendimiento de SQL, dificultando la optimización de consultas y el uso de índices. Una simple instrucción if sería mucho más eficiente. – Zyphrax

0

Este fuerza de trabajo, depende de si usted quiere decir el valor predeterminado definido en una restricción predeterminada, o en el código? Si la "restricción" falla, si el "código" funciona. Editar: te refieres a restricción. doh!

SELECT A,B,C,@prmSomeValue 
     FROM MyOtherTable 
     WHERE @prmSomeValue IS NOT NULL 
UNION ALL 
SELECT A,B,C,DEFAULT 
     FROM MyOtherTable 
     WHERE @prmSomeValue IS NULL 

Sea cual sea la forma en que desea hacerlo, especificando una columna en la cláusula INSERT es necesario un valor.

Así que su primera solución es hacer lo que has ...

+0

Me refería a la restricción predeterminada definida para la columna. Al igual que la primera mitad de mi declaración IF haría. Entonces esto no va a funcionar. Nota: No es absolutamente necesario especificar la columna en la cláusula INSERT. Puedo confiar en el orden de las columnas si es necesario para que esto funcione, aunque prefiero no hacerlo. – JohnFx

+0

JohnFx, si omite la lista de columnas, deberá tener la siguiente selección para tener el mismo número de columnas que coincidan con la tabla; de lo contrario, obtendrá: "Msg 213, nivel 16, estado 1, línea 1 El nombre de la columna o el número de valores proporcionados no coincide con la definición de la tabla ". Incluso si esto funcionó, robaría ser muy cauteloso al confiar en el orden de las columnas (su instinto es correcto, en mi humilde opinión). –

2

Diría que su método está bien. Una simple comprobación seguida de una inserción. Si está preocupado por DRY, encapsule la llamada para que se llame repetidamente.

Diría que las inserciones/actualizaciones en un db pueden ser costosas en algunas tablas (depende del objetivo del diseño) así que si tiene que escribir código adicional para manejar este escenario, entonces no veo ningún problema con el intercambio.

+0

+1, es bueno intentar y evitar bloques duplicados de código. Sin embargo, muchos programadores están tratando de llevar sus principios de OO y prácticas de codificación a T-SQL. A menudo resulta en consultas hermosas pero ineficientes. A veces es genial para el rendimiento escribir 5 consultas bastante similares en lugar de 1 semidinámica: mantener el código tal como está, se centraliza dentro de un procedimiento almacenado y es fácil de mantener. – Zyphrax

+0

¿No sabes que nos golpean esas cosas en la cabeza hasta el punto en que te duele físicamente tener prácticamente el mismo código duplicado en cada extremo de un condicional? =) Podrías tener razón, tal vez estoy pensando demasiado. – JohnFx

+1

Cuando solo tienes A o B, acepto que podría estar bien tal como está. Pero, ¿qué sucede cuando tienes cuatro de esas columnas? La cantidad de instrucciones INSERT posibles que necesita en función de qué parámetros son NULL explota muy rápidamente. A veces hay que equilibrar la eficiencia y el mantenimiento. –

0

Algo como esto podría funcionar (aunque no muy bonita):

INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD) 
SELECT A,B,C, 
    case when @prmSomeValue is null 
then 
     (SELECT text FROM syscomments WHERE id IN (SELECT cdefault FROM syscolumns 
      WHERE id = object_id('MyTable') AND cdefault > 0)) 
    else @prmSomeValue 
    end 
FROM MyOtherTable 
+2

¿Qué sucede si el valor predeterminado es en realidad una fórmula (como GETDATE() o NEWID())? se interpretará como una cadena, lo que lleva a un error o datos incorrectos, según el tipo de datos de la columna. Además, ¿no desea asegurarse de obtener la columna correcta (podría haber más de una columna donde cdefault> 0, lo que lleva a Msg 512 - la subconsulta arrojó más de un valor)? Para SQL Server 2008, ¿no debería usar vistas de catálogo en lugar de tablas del sistema en desuso? –

+0

Estaba pensando en un enfoque similar, pero estaba un poco preocupado por los efectos secundarios o los problemas de permisos asociados con el uso de tablas del sistema como este, pero supongo que se siente lo suficientemente sólido. ¿Alguna advertencia sobre esta técnica? – JohnFx

+0

¡Doh! Un gran problema con este enfoque. Si el valor predeterminado es una función, esto simplemente inserta el nombre de la función, en mi caso "getdate()". Buen intento sin embargo. – JohnFx

Cuestiones relacionadas