2008-10-02 13 views
20

Tengo que escribir un componente que vuelva a crear las tablas de SQL Server (estructura y datos) en una base de datos Oracle. Este componente también debe tomar nuevos datos ingresados ​​en la base de datos Oracle y copiarlos de nuevo en SQL Server.Oracle considera que las cadenas vacías son NULL mientras que SQL Server no. ¿Cómo se maneja mejor?

Traducir los tipos de datos de SQL Server a Oracle no es un problema. Sin embargo, una diferencia fundamental entre Oracle y SQL Server está causando un gran dolor de cabeza. SQL Server considera que una cadena en blanco ("") es diferente de un valor NULL, por lo que una columna char se puede definir como NOT NULL y aún así incluir cadenas en blanco en los datos.

Oracle considera una cadena en blanco a ser el mismo que un valor NULL, por lo que si una columna se define como charNOT NULL, no se puede insertar una cadena en blanco. Esto está haciendo que mi componente se rompa siempre que una columna NOT NULL char contenga una cadena en blanco en los datos originales de SQL Server.

Hasta ahora, mi solución ha sido no usar NOT NULL en ninguna de mis definiciones de tablas espejo de Oracle, pero necesito una solución más robusta. Tiene que ser una solución de código, por lo que la respuesta no puede ser "usar el producto SQL2Oracle de tal".

¿Cómo resolvería este problema?

Edición: esta es la única solución que he encontrado hasta ahora, y puede ayudar a ilustrar el problema. Como Oracle no permite "" en una columna NOT NULL, mi componente podría interceptar cualquier valor proveniente de SQL Server y reemplazarlo por "@" (solo por ejemplo).

Cuando agrego un nuevo registro a mi tabla Oracle, mi código tiene que escribir "@" si realmente quiero insertar un "", y cuando mi código copia la nueva fila a SQL Server, tiene que interceptar el "@" y en su lugar escriba "".

Espero que haya una manera más elegante.

Edición 2: ¿Es posible que haya una solución más simple, como alguna configuración en Oracle que consiga tratar cadenas en blanco al igual que todas las otras bases de datos importantes? ¿Y esta configuración también estaría disponible en Oracle Lite?

+0

En cuanto a su Edición 2: Existe una solución horrible que implica el uso de un CLOB para todas estas columnas. Oracle distingue entre NULL y un CLOB vacío. Puede almacenar el CLOB en línea en la tabla para reducir el impacto en el rendimiento. No haría esto (ver mi respuesta real a continuación). –

+0

@WW .: tiene toda la razón, esa es una solución horrible. – MusiGenesis

+1

Holy cow. SQL Server tiene su propia parte de estupidez, pero esto no tiene sentido. (¿Es esto todavía con Oracle 2014+ actual, y ha habido una configuración "ANSI NULL" para ello? SQL Server tuvo algunos problemas como este manejo previo "ANSI NULL") – user2864740

Respuesta

10

No veo una solución fácil para esto.

Quizás pueda almacenar sus valores como uno o más espacios en blanco -> ' ', que no son NULLS en Oracle, o realizar un seguimiento de este caso especial a través de campos/tablas adicionales, y una capa de adaptador.

3

¿Debe permitir cadenas vacías en el sistema SQL Server? Si puede agregar una restricción al sistema SQL Server que no permite cadenas vacías, esa es probablemente la solución más fácil.

+0

Ojalá. La base de datos de SQL Server no está bajo mi control. – MusiGenesis

1

Si está migrando datos, puede que tenga que sustituir un espacio por una cadena vacía. No es muy elegante, pero viable. Esta es una desagradable "característica" de Oracle.

+2

Quería titular mi pregunta "¿por qué en la Tierra equivale Oracle cadenas en blanco con nulos?" – MusiGenesis

+1

Después de pensar en esto por unos años, creo que Oracle lo hizo bien. SQL Server define NULL desde la perspectiva de un programador: para un programador, una cadena en blanco sigue siendo * algo *, y muy distinta de una variable nula en la mayoría de los lenguajes de código. Oracle lo define desde una perspectiva de usuario final (representado por el diseñador de la base de datos en este caso); si, como diseñador de bases de datos, especifico que una columna NO es NULA, lo hago para que los usuarios (incluidos los programadores) pongan un valor real allí para cada fila. Realmente no puedo pensar en un solo ejemplo de una mesa que haya diseñado donde hubiera querido ... – MusiGenesis

+1

... personas poniendo cadenas en blanco (por ejemplo, código postal, número de seguro social, etc.) en mi NOT NULL campo. No creo que siquiera fuera consciente de que SQL Server * permite * espacios en blanco en campos NOT NULL hasta que leo esta pregunta. TL; DR: SQL Server hack permite a los programadores burlar las intenciones de los diseñadores de db. – MusiGenesis

9

Mi solución típica sería añadir una restricción en SQL Server obligando a todos los valores de cadena en las columnas afectadas para tener una longitud mayor que 0:

CREATE TABLE Example (StringColumn VARCHAR(10) NOT NULL) 

ALTER TABLE Example 
ADD CONSTRAINT CK_Example_StringColumn CHECK (LEN(StringColumn) > 0) 

Sin embargo, como usted ha dicho, no tiene ningún control sobre la base de datos SQL.Como tal que realmente tiene cuatro opciones (como yo lo veo):

  1. tratar los valores de cadena vacía como no válidos, no pase por esos registros, alertar a un operador y registrar los registros de alguna manera que hace que sea fácil de corregir manualmente/re -entrar.
  2. Convertir valores de cadenas vacías en espacios.
  3. Convierta valores de cadenas vacías a un código (es decir, "LEGADO" o "VACÍO").
  4. Las transferencias de restitución que encuentran valores de cadenas vacías en estas columnas, luego ejercen presión sobre el propietario de la base de datos de SQL Server para corregir sus datos.

El número cuatro sería mi preferencia, pero no siempre es posible. La acción que realice realmente dependerá de lo que necesiten los usuarios de Oracle. En definitiva, si no se puede hacer nada con la base de datos SQL, explico el problema a los propietarios del sistema de Oracle, explique las opciones y las consecuencias y tomen la decisión :)

NOTA: Creo en este caso SQL Server realmente muestra el comportamiento "correcto".

3

Su desagradable y podría tener efectos secundarios inesperados ... pero podría simplemente insertar "chr (0)" en lugar de "".

drop table x 

drop table x succeeded. 
create table x (id number, my_varchar varchar2(10)) 

create table succeeded. 
insert into x values (1, chr(0)) 

1 rows inserted 
insert into x values (2, null) 

1 rows inserted 
select id,length(my_varchar) from x 

ID      LENGTH(MY_VARCHAR)  
---------------------- ---------------------- 
1      1      
2            

2 rows selected 

select * from x where my_varchar is not null 

ID      MY_VARCHAR 
---------------------- ---------- 
1      
+0

Esto es "muy desagradable". Lleva una cuerda con un solo espacio al extremo. – user2864740

3

NOT NULL es una restricción de la base de datos utilizada para dejar de incluir datos no válidos en su base de datos. Esto no sirve para nada en su base de datos Oracle, por lo que no lo tendría.

Creo que debería continuar permitiendo NULLS en cualquier columna de Oracle que refleje una columna SqlServer que se sabe que contiene cadenas vacías.

Si existe una diferencia lógica en la base de datos SqlServer entre NULL y cadena vacía, entonces necesitaría algo adicional para modelar esta diferencia en Oracle.

+0

Creo que esta es la única respuesta sensata aquí. Básicamente, si los desarrolladores de la base de datos del servidor sql están almacenando cadenas vacías en columnas NOT NULL, estas columnas deberán ser anulables en Oracle db. Así que MusiGenesis tuvo la respuesta correcta desde el principio y no tuvo que hacer nada para resolver el problema. – MikeKulls

2

Para aquellos que piensan que una cadena vacía y una cadena vacía deben considerarse iguales. Un nulo tiene un significado diferente de una cadena vacía. Captura la diferencia entre 'indefinido' y 'conocido como vacío'. Como ejemplo, un registro puede haber sido creado automáticamente, pero nunca validado por la entrada del usuario, y así recibir un 'nulo' en la expectativa de que cuando un usuario lo valida, se configurará como vacío. Prácticamente es posible que no deseemos desencadenar la lógica en un nulo, pero es posible que deseemos en una cadena vacía. Esto es análogo al caso de una casilla de 3 estados de Sí/No/Indefinido.

Tanto SQL como Oracle no lo tienen del todo correcto. Un espacio en blanco no debe satisfacer una restricción 'no nula', y existe la necesidad de que una cadena vacía se trate de manera diferente a como se trata un valor nulo.

0

He escrito una explicación sobre cómo Oracle maneja valores nulos en mi blog hace un tiempo. Verifíquelo aquí: http://www.psinke.nl/blog/hello-world/ y avíseme si tiene más preguntas. Si tiene datos de una fuente con valores vacíos y debe convertir a una base de datos Oracle en columnas no son NULL, hay 2 cosas que puede hacer:

  • eliminar la restricción no nula de la columna de Oracle
  • Verifique cada columna individual si es aceptable colocar una '' o 0 o una fecha ficticia en la columna para poder guardar sus datos.
0

Bueno, el punto principal que consideraría es la ausencia de tareas cuando algún campo puede ser nulo, el mismo campo puede ser cadena vacía y la lógica de negocios requiere distinguir estos valores. Así que me gustaría hacer esta lógica:

  • cheque MSSQL si la columna tiene restricción NOT NULL
  • cheque MSSQL si la columna tiene CHECK (columna <> '') o las mismas limitaciones

Si ambos están cierto, hacer que la columna de Oracle NO sea NULL. Si alguno es verdadero, haz la columna Oracle NULL. Si ninguno es verdadero, levante la excepción DISEÑO NO VÁLIDO (o tal vez la ignore, si es aceptable para esta aplicación).

Cuando envíe datos de MSSQL a Oracle, simplemente no haga nada especial, todos los datos se transferirán a la derecha. Al recuperar datos en MSSQL, cualquier información no nula debe enviarse como está. Para cadenas nulas, debe decidir si debe insertarse como nulo o como cadena vacía. Para hacer esto, debe verificar nuevamente el diseño de la tabla (o recordar el resultado anterior) y ver si tiene una restricción NOT NULL. Si tiene - use cadena vacía, si no lo ha hecho - use NULL. Simple e inteligente.

A veces, si trabajas con aplicaciones desconocidas e impredecibles, no puedes verificar la existencia de una restricción {cadena vacía} debido a varias formas de la misma. Si es así, puede usar lógica simplificada (hacer que las columnas de Oracle sean siempre anulables) o verificar si puede insertar cadena vacía en la tabla MSSQL sin error.

2

Iría con una columna adicional en el lado del oráculo. Haga que su columna permita nulos y tenga una segunda columna que identifique si el lado del Servidor SQL debe obtener un valor nulo o una cadena vacía para la fila.

0

Aunque, en su mayor parte, estoy de acuerdo con la mayoría de las otras respuestas (no va a entrar en una discusión acerca de cualquier estoy de acuerdo con - no es el lugar para que :))

hago notar que la OP mencionó lo siguiente:

"Oracle considera que una cadena en blanco es igual que un valor NULO, por lo que si una columna char se define como NOT NULL, no puede insertar una cadena en blanco."

Llamando específicamente CHAR, y no VARCHAR2. Por lo tanto, hablar de una "cadena vacía" de longitud 0 (es decir, '') es irrelevante. Si ha declarado el CHAR como, por ejemplo, CHAR (5), simplemente agrega un espacio a la cadena vacía que entra, Oracle lo va a rellenar de todos modos. Terminarás con una cadena de 5 espacios.

Ahora, si OP significa VARCHAR2, bueno, eso es una bestia completamente diferente, y sí, la diferencia entre cadena vacía y NULL se vuelve relevante.

SQL> drop table junk; 

    Table dropped. 

    SQL> 
    SQL> create table junk (c1  char(5) not null); 

    Table created. 

    SQL> 
    SQL> insert into junk values ('hi'); 

    1 row created. 

    SQL> 
    SQL> insert into junk values (' '); 

    1 row created. 

    SQL> 
    SQL> insert into junk values (''); 
    insert into junk values ('') 
          * 
    ERROR at line 1: 
    ORA-01400: cannot insert NULL into ("GREGS"."JUNK"."C1") 


    SQL> 
    SQL> insert into junk values (rpad('', 5, ' ')); 
    insert into junk values (rpad('', 5, ' ')) 
          * 
    ERROR at line 1: 
    ORA-01400: cannot insert NULL into ("GREGS"."JUNK"."C1") 


    SQL> 
    SQL> declare 
    2 lv_in varchar2(5) := ''; 
    3 begin 
    4  insert into junk values (rpad(lv_in||' ', 5)); 
    5 end; 
    6/

    PL/SQL procedure successfully completed. 

    SQL> 
Cuestiones relacionadas