2012-01-20 25 views
5

Digamos que tengo estas dos tablas con las mismas columnas. Use su imaginación para hacerlos más grandes:¿Cómo fusiono dos tablas sin nombrar todas las columnas?

USER_COUNTERPARTY: 
ID |Name      |Credit Rating |Sovereign Risk |Invoicing Type 
----+----------------------------+-----------------+------------------+--------------- 
1 |Nat Bank of Transnistria |7    |93    |Automatic 
2 |Acme Ltd.     |25    |12    |Automatic 
3 |CowBInd LLP.    |49    |12    |Manual 

TEMP: 
ID |Name      |Credit Rating |Sovereign Risk |Invoicing Type 
----+----------------------------+-----------------+------------------+--------------- 
2 |Acacacme Ltd.    |31    |12    |Manual 
4 |Disenthralled Nimrod Corp. |31    |52    |Automatic 

y quiero fusionarlas en una sola, la sustitución con el segundo todo lo que tiene el mismo ID en la primera, y la inserción de todo lo que no está allí. Puedo usar esta afirmación:

MERGE INTO USER_COUNTERPARTY C 
USING TEMP T 
ON (C.COUNTER_ID = T.COUNTER_ID) 
WHEN MATCHED THEN UPDATE SET 
    C.COUNTER_NAME = T.COUNTER_NAME, 
    C.COUNTER_CREDIT = T.COUNTER_CREDIT, 
    C.COUNTER_SVRN_RISK = T.COUNTER_SVRN_RISK, 
    C.COUNTER_INVOICE_TYPE = T.COUNTER_INVOICE_TYPE 
WHEN NOT MATCHED THEN INSERT VALUES (
    T.COUNTER_ID, 
    T.COUNTER_NAME, 
    T.COUNTER_CREDIT, 
    T.COUNTER_SVRN_RISK, 
    T.COUNTER_INVOICE_TYPE); 

Que es lo suficientemente agradable, pero tenga en cuenta que debo nombrar cada una de las columnas. ¿Hay alguna manera de combinar estas tablas sin tener que nombrar todas las columnas? Oracle documentation insiste en que utilizo los nombres de las columnas después de 'insertar' y 'establecer' en una fusión, por lo que podría ser necesaria alguna otra declaración. El resultado debe ser el siguiente:

ID |Name      |Credit Rating |Sovereign Risk |Invoicing Type 
----+----------------------------+-----------------+------------------+--------------- 
1 |Nat Bank of Transnistria |7    |93    |Automatic 
2 |Acacacme Ltd.    |31    |12    |Manual 
3 |CowBInd LLP.    |49    |12    |Manual 
4 |Disenthralled Nimrod Corp. |31    |52    |Automatic 

En caso de que ayuda estoy pegando esto aquí:

CREATE TABLE USER_COUNTERPARTY 
(COUNTER_ID    INTEGER  NOT NULL PRIMARY KEY, 
    COUNTER_NAME   VARCHAR(38), 
    COUNTER_CREDIT   INTEGER, 
    COUNTER_SVRN_RISK  INTEGER, 
    COUNTER_INVOICE_TYPE VARCHAR(10)); 

INSERT ALL 
INTO USER_COUNTERPARTY VALUES (1, ‘Nat Bank of Transnistria’, 7, 93, ‘Automatic’) 
INTO USER_COUNTERPARTY VALUES (2, ‘Acme Ltd.’, 25, 12, ‘Manual’) 
INTO USER_COUNTERPARTY VALUES (3, ‘CowBInd LLP.’, 49, 12, ‘Manual’) 
SELECT * FROM DUAL; 

CREATE TABLE TEMP AS SELECT * FROM USER_COUNTERPARTY; 
DELETE FROM TEMP; 

INSERT ALL 
INTO TEMP VALUES (2, ‘Conoco Ltd.’, 25, 12, ‘Automatic’) 
INTO TEMP VALUES (4, ‘Disenthralled Nimrod Corp.’, 63, 12, ‘Manual’) 
SELECT * FROM DUAL; 
+0

Puede consultar el diccionario de datos (por ejemplo, 'USER_TAB_COLUMNS') para generar la lista de nombres de columna. –

+0

Es verdad, puedes. ¿Pero entonces qué? Tal vez tenga que ponerlos en una colección, recorrerlos y usar una inserción dinámica. Para actualizar todas las columnas, creo que eliminar e insertar sería mejor. Pero tendré en cuenta el diccionario de datos. – jalopezp

+0

"¿Pero entonces qué?" Como dije: * generar la lista de nombres de columna *. p.ej. 'SELECT ',' || column_name FROM user_tab_columns WHERE table_name = 'MYTABLE' ORDER BY column_id;' - luego copie y pegue los resultados en su declaración de fusión. Claro, no es bonito, y debe volver a hacerse si el esquema cambia, pero funciona. Ciertamente, no estoy hablando de generación de código en tiempo de ejecución :) –

Respuesta

2

creo que la única opción que tiene que evitar el uso de los nombres de las columnas es de dos estados separados:

delete from USER_COUNTERPARTY UC 
     where exists 
       (select null 
       from TEMP T 
       where T.COUNTER_ID = UC.COUNTER_ID); 

insert into USER_COUNTERPARTY UC 
    select * 
    from TEMP T 
    where not exists 
      (select null 
       from USER_COUNTERPARTY UC 
      where T.COUNTER_ID = UC.COUNTER_ID); 
+0

Sí, creo que esta es la manera de hacerlo. – jalopezp

+0

¿Pero por qué usas 'select null' en lugar de 'select *'? ¿Esto es para la eficiencia? ¿Debo también estar haciendo esto? – jalopezp

+0

El 'seleccionar' en la sub-consulta a' exists' está ahí para indicar si se devuelve una fila o no, no importa qué columnas se devuelvan. Algunas personas usarán 'seleccionar 1', otras' seleccionar * ', no hay diferencia real. –

1

Usted podría tratar de usar una declaración de la Unión envuelto de esta manera:

SELECT (*) FROM 
(SELECT * FROM Table1 WHERE ID NOT IN (SELECT ID FROM Table2) 
UNION ALL 
SELECT * FROM Table2) 
ORDER BY 1 
0

Si tiene valores predeterminados para las columnas (y desea usar estos valores predeterminados), puede omitir los de su instrucción de inserción, pero de lo contrario, debe especificar cada columna para la que desea insertar o actualizar los valores.

No hay una abreviatura como * para SELECT.

0

he encontrado el problema descrito y la forma en que he abordado es muy baja tecnología, pero el pensamiento Lo compartiría en caso de que desencadenara otras ideas para las personas.

Tomé los nombres de las columnas (los extraje de la tabla DDL en el desarrollador de SQL, pero también uso el método en la tabla tab_columns) y los inserté en una hoja de cálculo de Excel. Luego eliminé las declaraciones de Varchar, etc. (usando la función Excel de texto a columnas y luego solo borré la (s) columna (s) en que terminaron las declaraciones varchar, number, etc.), así que simplemente dejé los nombres de los campos. Luego inserté una fórmula en la siguiente columna de Excel, = "dest". & A2 & "= src." & A2 & "," y se rellenó para los 110 campos, y luego en una nueva columna de Excel, se utilizó = A2 & "," y en una columna nueva, = "src." & A2 & ",", llenando de nuevo para todos los campos. Luego, en una hoja de SQL, I entran:

merge into <schema>.<destination_table> dest 
    using <schema>.<source_table> src 
on (dest.<link> = src.<link>) 
when matched then update set 
(<copy all of the first column, 
    not including the linking fields and removing the comma at the end>) 
when not matched then insert 
(<copy and paste the second column from Excel, and remove the final comma>) 
values 
(<copy and paste the third column from Excel and remove the final comma>) 

También tengo una versión para fusionar tablas con diferentes nombres de columna, sino que implica un paso adicional de mapear los campos de la hoja de cálculo de Excel.

Encuentro que necesito usar declaraciones combinadas para lo que hago - Encuentro Merge en un inmenso ahorro de tiempo en comparación con la actualización donde existe.

0

Me encontré con el mismo problema y escribí un procedimiento que obtiene una lista de todas las columnas de la tabla y construye una consulta dinámica SQL para hacer una actualización sin nombrar todas las columnas.

PROCEDURE update_from_table(
    p_source VARCHAR2, -- Table to copy all columns from 
    p_target VARCHAR2, -- Table to copy into 
    p_id_name VARCHAR2 -- Primary key name 
) 
    AS 
v_sql VARCHAR2(4096) := 'UPDATE ' || p_target || ' t1 SET ('; 
v_sql_src VARCHAR2(4096) := ') = (SELECT '; 
v_sql_end VARCHAR2(4096) := ' FROM '|| p_source ||' t2 WHERE t1.'||p_id_name||' = t2.'||p_id_name||') 
WHERE EXISTS (
SELECT 1 
    FROM '|| p_source ||' t2 
WHERE t1.'||p_id_name||' = t2.'||p_id_name||')'; 
v_first BOOLEAN := TRUE; 
BEGIN 
FOR col IN 
(select column_name from user_tab_columns 
    where table_name = p_source 
) 
LOOP 
    IF NOT v_first THEN 
    v_sql:= v_sql || ', '; -- adding comma before every arg except first 
    v_sql_src := v_sql_src || ', '; 
    ELSE 
    v_first := FALSE; 
    END IF; 
    v_sql:= v_sql || col.column_name ; 
    v_sql_src:= v_sql_src || col.column_name ; 
END LOOP; 

v_sql := v_sql || v_sql_src || v_sql_end; 
EXECUTE IMMEDIATE v_sql; 
END update_from_table; 

Y entonces se fusionan en 2 pasos:

-- Insert not matching records 
INSERT INTO USER_COUNTERPARTY 
      SELECT * 
      FROM TEMP WHERE COUNTER_ID NOT IN (
     SELECT USER_COUNTERPARTY.COUNTER_ID 
     FROM USER_COUNTERPARTY 
     JOIN TEMP ON TEMP.COUNTER_ID = USER_COUNTERPARTY.COUNTER_ID); 
-- Update matching records 
update_from_table('TEMP', 'USER_COUNTERPARTY', 'COUNTER_ID'); 
Cuestiones relacionadas