2012-01-20 22 views
5

Powiedzmy, że mam te dwie tabele z tymi samymi kolumnami. Użyj wyobraźni, aby je powiększyć:Jak scalić dwie tabele bez nazywania wszystkich kolumn?

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 

i chcę, aby połączyć je w jeden, zastępując jednego z drugim, co ma ten sam identyfikator w pierwszym i wstawienie cokolwiek nie ma. Mogę użyć tego stwierdzenia:

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); 

Co jest wystarczająco dobre, ale zauważ, że muszę nazwać każdą z kolumn. Czy istnieje sposób na połączenie tych tabel bez konieczności wymieniania wszystkich kolumn? Oracle documentation twierdzi, że używam nazw kolumn po "wstawieniu" i "zestawie" w połączeniu, więc może być potrzebne inne zdanie. Wynik powinien być tak:

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 

W przypadku pomaga mi wklejając ten tutaj:

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

Można kwerendy słownika danych (np 'USER_TAB_COLUMNS'), aby wygenerować listę nazw kolumn. –

+0

Prawda, możesz. Ale co wtedy? Być może będę musiał umieścić je w kolekcji, przejrzeć je i użyć dynamicznej wstawki. Aby zaktualizować wszystkie kolumny, myślę, że usuwanie i wstawianie byłoby lepsze. Ale będę pamiętał o słowniku danych. – jalopezp

+0

"Ale co wtedy?" Jak już powiedziałem: * wygeneruj listę nazw kolumn *. na przykład 'SELECT ',' || column_name FROM user_tab_columns WHERE nazwa_tabeli = 'MYTABLE' ORDER BY id_kolumny;' - następnie kopiujesz i wklejasz wyniki do swojej instrukcji scalania. Jasne, to nie jest ładne i musi zostać powtórzone, jeśli schemat się zmieni, ale działa. Na pewno nie mówię o generowaniu kodu w czasie wykonywania :) –

Odpowiedz

2

wierzę jedyną opcję trzeba unikać używania nazwy kolumn są dwa oddzielne sprawozdania:

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

Tak, myślę, że to jest sposób na zrobienie tego. – jalopezp

+0

Ale dlaczego używasz "wybierz null" zamiast "wybierz *"? Czy to dla wydajności? Czy powinienem też to robić? – jalopezp

+0

"Wybierz" w pod-zapytaniu do "istnieje" jest po prostu tam, aby wskazać, czy wiersz jest zwracany, czy nie, nie ma znaczenia, które kolumny są zwracane. Niektórzy ludzie użyją 'select 1', inni' select * ', nie ma prawdziwej różnicy. –

1

można spróbować za pomocą owinięte oświadczenia Union takiego:

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

Jeśli masz domyślne wartości dla kolumn (i chcesz użyć tych wartości domyślnych), możesz pominąć te z instrukcji wstawiania, ale poza tym musisz określić każdą kolumnę, do której chcesz wstawić lub zaktualizować wartości.

Nie ma stenogramu takiego jak * dla SELECT.

0

mam natknąć na problem opisany, a sposób, w jaki zmierzyły go jest bardzo niski tech, ale myśl Dzieliłbym się, gdyby wywołał inne pomysły dla ludzi.

Wziąłem nazwy kolumn (wyodrębniam je z tabeli DDL w SQL developer, ale także używam metody z tabeli tab_columns) i wstawiłem je do arkusza kalkulacyjnego Excel. Następnie usunąłem instrukcje Varchar itp. (Używając funkcji text to columns Excel, a następnie po prostu usunięto kolumny, z których wynikały instrukcje varchar, number itp.), Więc opuściły one nazwy pól. Następnie wstawiłem formułę w następnej kolumnie programu Excel = "dest". & A2 & "= src." & A2 & "," i wypełnione dla wszystkich 110 pól, a następnie w nowej kolumnie Excel, używane = A2 & "," oraz w nowej kolumnie, = "src". & A2 & ",", ponownie wypełniając wszystkie pola. Następnie w arkuszu SQL, wejdę:

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>) 

Mam też wersję dla łączenia tabel z różnymi nazwami kolumn, ale to wiąże się dodatkowy etap mapowania pól w arkuszu Excel.

Uważam, że do tego, co robię, potrzebuję użyć instrukcji scalania - uważam, że Merge w ogromnym oszczędzaniu czasu w porównaniu z aktualizacją, o ile istnieje.

0

Natrafiłem na ten sam problem i napisałem procedurę, która pobiera listę wszystkich kolumn tabeli i tworzy dynamiczne zapytanie SQL, aby wykonać aktualizację bez nazywania wszystkich kolumn.

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; 

I wtedy nie łączą się w 2 etapach:

-- 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');