error de Oracle? declaraciones de SELECT no hay incautos, INSERT de SELECT tiene filas duplicadas

StackOverflow https://stackoverflow.com/questions/1868417

  •  18-09-2019
  •  | 
  •  

Pregunta

Me estoy poniendo un comportamiento extraño de una instancia de Oracle que estoy trabajando. Esta es 11gR1 en Itanium, sin RAC, nada especial. En última instancia me estoy moviendo datos de una instancia de Oracle a otra en un escenario de almacenamiento de datos.

Tengo una vista semi-complejo se ejecutan sobre un enlace DB; 4 combinaciones internas más grandes mesas-ish y 5 se une a izquierda sobre tablas de tamaño medio.

Aquí está el problema: cuando pruebo la vista en SQL Developer (o SQL * Plus) parece bien, no hay duplicación de ningún tipo. Sin embargo, cuando en realidad utilizar la vista para insertar datos en una tabla consigo un gran número de incautos.

EDIT: - Los datos se va en una tabla vacía. Todas las tablas de la consulta son en el enlace de base de datos. La única cosa pasado a la consulta es una fecha (por ejemplo, INSERTAR EN objetivo SELECT * FROM vista DONDE view.datecol = dQueryDate) -

He intentado añadir una función ROW_NUMBER () a la instrucción de selección, dividido por el PK para la vista. Todas las filas vienen numerados atrás como 1. Una vez más, sin embargo, la misma declaración funcionar como un inserto genera los mismos engañados como antes y ahora numerados convenientemente. El número de filas engañados no es lo mismo por tecla. Existen algunos registros 4 veces algunos sólo existen una vez.

Me parece que este comportamiento para ser extremadamente desconcertante. :) Me recuerda a trabajar con Teradata, donde tiene SET tablas (filas únicas solamente) y tablas conjunto múltiple (filas duplicadas permitidas) pero Oracle no tiene esa funcionalidad.

Un selecto que devuelve filas para el cliente debe comportarse de forma idéntica a una que inserta las filas a otro lugar. No me puedo imaginar una razón legítima para que esto suceda, pero tal vez estoy sufriendo de una falta de imaginación. ;)

Me pregunto si alguien más ha experimentado esto o si se trata de un error en esta plataforma.

SOLUCIÓN

Gracias a @Gary, yo era capaz de llegar al fondo de esto usando "Explain Plan para {} mi consulta;" y "SELECT * FROM TABLE (dbms_xplan.display);". La explicar que realmente se acostumbra para el inserto es muy diferente de la SELECT.

Para el SELECT la mayor parte de las operaciones del plan son 'ACCESO POR ROWID TABLA ÍNDICE' e 'índice UNIQUE SCAN'. El bloque 'Predicado de la información' contiene todas las juntas y los filtros de la consulta. Al final dice "Nota - totalmente comunicado a distancia" .

Para el INSERT no hay ninguna referencia a los índices. El bloque 'predicado información' está a sólo tres líneas y muestra un nuevo bloque '' SQL remoto declaraciones 9 .

La base de datos se ha dividido mi consulta en subconsultas 9 y luego intenta unirse a ellos a nivel local. Mediante la ejecución de los más pequeños selecciona He localizado la fuente de los duplicados.

Creo que esto es error en el compilador de Oracle alrededor de enlaces remotos. Crea defectos lógicos cuando se re-escribir el código SQL. Básicamente el compilador no está aplicando correctamente la cláusula WHERE. Sólo estaba probando y se lo di una lista IN de 5 teclas para traer de vuelta. SELECT devuelve 5 filas. INSERT pone 77,000+ filas en el objetivo y ignora totalmente la lista IN.

{Todavía estás buscando una manera de forzar el comportamiento correcto, voy a tener que pedir el fin de crear en la base de datos remota aunque eso no es ideal desde el punto de vista del desarrollo. Voy a editar esto cuando tengo que trabajar ...}

¿Fue útil?

Solución

Parece ser Oracle Bug, hemos hallado que este workarround siguiente: Si usted quiere que su "insert into select ..." el trabajo como en su "select ...", se puede empacar su selecto en un sub selecto.

Por ejemplo:

select x,y,z from table1, table2, where ...

-> ningún duplicado

insert into example_table
select x,y,z from table1, table2, where ...

-> error duplicado

insert into example_table
select * from (
       select x,y,z from table1, table2, where ...
)

-> ningún duplicado

Regards

Otros consejos

Una cosa que viene a la mente es que en general, un plan de optimizador para un SELECT preferirá un plan para dar FIRST_ROWS filas de nuevo a la persona que llama temprano, pero un INSERT ... SELECT preferirá un plan ALL_ROWS ya que va a tener para entregar el conjunto de datos completo. Me echa los planes de consulta utilizando DBMS_XPLAN.DISPLAY_CURSOR (utilizando el SQL_ID de V $ SQL).

  

Tengo una vista semi-complejo consecutivo   sobre un enlace DB; 4 interior se une a más de   mesas de gran ish y 5 se une a más de izquierda   tablas de tamaño medio.   ...   Todas las tablas de la consulta están en   el enlace de la base de datos

Una vez más, un potencial de problemas in situ. Si todas las tablas de la SELECT estaban en el otro extremo del enlace DB, toda la consulta se envía a la base de datos remota y el conjunto de resultados devuelto. Una vez que se lanza el inserto en, es más probable que la base de datos local se hará cargo de la consulta y tirar todos los datos de las tablas hijo más. Pero eso puede depender de si la vista se define en la base de datos local o la base de datos remota. En este último caso, en lo que se refiere al optimizador local, sólo hay un objeto remoto y obtiene datos de eso, y la base de datos remota va a hacer la unión.

¿Qué pasa si sólo ir a la remota base de datos y hacer el INSERT en una mesa allí?

Esto es un error en el manejo de Oracle de une a través de enlaces de base de datos. Tengo una situación más simple que no implica un INSERT SELECT frente. Si me quedo mi consulta remota, consigo filas duplicadas, pero si lo ejecuto a nivel local, no lo hago. La única diferencia entre las consultas es la "@ ..." añadido a las tablas de la consulta a distancia. Yo estoy consultando una base de datos 9i desde una base de datos usando 10,2 Oracle SQL Developer 3.0.

Esto incluso más estúpido que ese error en Oracle que le impide unirse a las tablas con más de 1000 columnas en total, que es muy fácil de hacer cuando se consulta el sistema ERP. Y no, el mensaje de error no es nada acerca de las tablas que tienen demasiadas columnas.

Es casi tan estúpido como el otro fallo base de datos Oracle que prohíbe las tablas que contienen consulta de localizadores de LOB utilizando la sintaxis ANSI. Sólo las obras de sintaxis de Oracle!

Varias opciones se me ocurren.

  1. Los incautos que se ven ya estaban en la tabla de destino ??

  2. Si en su Selección, se hace referencia a la tabla que está insertando en, (?), A continuación, el inserto está interactuando con la selección en el resto combinado

    Insertar ... SELECT ... FROM ...

De tal manera (productos cartesianos?) Como para crear los duplicados

No puedo evitar pensar que tal vez usted está experimentando un efecto secundario de alguna otra cosa relacionada con la tabla. ¿Hay factores desencadenantes que se pueden manipular datos?

¿Cómo se determina que no hay duplicados en la tabla original?

Como otros han señalado esta parece ser la explicación más simple para este extraño comportamiento.

Compruebe sus JOINs cuidadosamente. Potencialmente usted no tiene duplicados en las tablas individuales, pero subespecificada juntas pueden causar CROSS JOINs inadvertidos para que su conjunto de resultados tiene duplicados debido a la multiplicidad y, cuando se inserta, esto viola una restricción de unicidad en la tabla de destino.

Lo que hago en este caso es para anidar la consulta en una vista o CTE y tratar de detectar los duplicados directamente de la SELECT:

WITH resultset AS (
    -- blah, blah
)
SELECT a, b, c, COUNT(*)
FROM resultset
GROUP BY a, b, c
HAVING COUNT(*) > 1

Yo sugeriría conseguir un plan de la consulta que se está ejecutando y en busca de una combinación cartesiana en allí. Esto podría indicar una condición de falta que está causando filas duplicadas.

AS @Pop ya ha sugerido este comportamiento puede ocurrir si está utilizando un nombre de usuario diferente en SQLPlus al inicio de sesión cuando el inserto está en marcha. (Esto es, si la otra entrada tiene una tabla / vista / sinónimo con el mismo nombre)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top