2016-08-11 5 views
2

Ich muss Update einer relativ sehr großen Tabelle (80M Datensätze) Invoice_Payment durchführen. Es sollte Daten von einer anderen Tabelle Invoice_Payment_updated aktualisieren, die 10% -15% von Invoice_Payment in Zeilenanzahl ist. Zur Veranschaulichung bitte einen Blick auf die folgende Demo-Tabellen nehmen:Der beste Ansatz zur Verbesserung der Abfrageleistung bei der Aktualisierung großer Tabelleninhalte

Invoice_Payment       Invoice_Payment_updated 
    ---------------       -----------------------      
    Customer_id Invoice_no    Id Cust_id Invoice_no  
    10   10100001     1  10  20200100   
    11   10100002     2  11  20200101   
    12   10100003 
    13   10100004 

Ich weiß Merge in der Regel eine UPSERT verwendet wird, um auszuführen und es dauert länger mehrmals als Aussage äquivalent Aktualisierung auszuführen. Im Vergleich dazu gibt es jedoch Fälle, in denen eine normale update-Anweisung mit mehreren Unterabfragen zu einer geringeren Leistung führt.

MERGE INTO Invoice_Payment ip 
USING (SELECT ipu.Cust_id, ipu.Invoice_no from Invoice_Payment_updated ipu 
     INNER JOIN Invoice_Payment ip ON ip.Customer_id = ipu.Cust_id 
     WHERE ipu.Cust_id = ip.Customer_id and ipu.Invoice_no <> ip.Invoice_no) t 
ON (ip.Customer_id = t.Cust_id) 
WHEN MATCHED THEN 
UPDATE SET ip.Invoice_no = t.Invoice_no; 

Um die Leistung zu verbessern, kann ich Batch auf die Updates ROWCOUNT verwenden, aber das wird die Ausführung nicht beschleunigen, wird es nur mit der Verringerung Gesamtsperr helfen.

einfache Update-Anweisung Im Anschluss an die gleiche Leistung zurückgibt:

eine sehr clevere eine SQL Merge und aktualisieren ist
UPDATE Invoice_Payment 
SET Invoice_no = (SELECT ipu.Invoice_no 
        FROM Invoice_Payment_updated ipu 
        WHERE ipu.Cust_id = Invoice_Payment.Customer_id 
        AND ipu.Invoice_no <> Invoice_Payment.Invoice_no) 

WHERE EXISTS (SELECT 1 
       FROM Invoice_Payment_updated ipu 
       WHERE ipu.Cust_id = Invoice_Payment.Customer_id 
       AND ipu.Invoice_no <> Invoice_Payment.Invoice_no); 

Idee zu verwenden, aber ich hörte, dass sie beide in Performance-Probleme schlägt fehl, wenn ich viele Datensätze aktualisieren müssen (dh über 75M) in einem großen und breiten Tisch. Darüber hinaus ist das Wiederherstellen der gesamten Tabelle eine Menge IO-Last, ganz zu schweigen davon, dass sie viel Platz in Anspruch nehmen wird, um die Tabelle aufgrund von Unterabfragen vorübergehend mehrmals speichern zu lassen.

Ein weiterer Ansatz, um dieses Problem anhand der temporäre Tabelle zu beheben:

CREATE TABLE tmp (
    Cust_id int, 
    Invoice_no int); 

INSERT INTO tmp_stage VALUES 
(SELECT ipu.Cust_id, ipu.Invoice_no FROM Invoice_Payment_updated ipu 
INNER JOIN Invoice_Payment ip ON ip.Customer_id = ipu.Cust_id 
WHERE ipu.Cust_id = ip.Customer_id and ipu.Invoice_no <> ip.Invoice_no); 

UPDATE (SELECT tmp.Cust_id, ip.Customer_id, tmp.Invoice_no, tgt.Invoice_no 
     FROM tmp INNER JOIN Invoice_Payment ip 
     ON tmp.Cust_id = ip.Customer_id) 
SET tmp.Invoice_no = ip.Invoice_no; 

I, die ein, um herauszufinden, will, ist besser im Fall von mehreren Unterabfragen verwenden?

Alle Gedanken sind willkommen und eine völlig andere Lösung des ursprünglichen Problems wird sehr geschätzt.

+1

Hallo, bitte lesen Sie durch diesen Artikel, es wird eine Menge helfen: http://www.orafaq.com/node/2450 – Thomas

+0

@Thomas Seien Sie vorsichtig mit diesem Artikel, es ist einfach, die falsche Schlussfolgerung zu ziehen. So wie die Informationen präsentiert werden, mag man denken, dass # 8 die schnellste ist, aber # 7 ist genauso schnell. Der letzte Anhang widerspricht den Ergebnissen. –

+0

@JonHeller das ist warum ich schrieb "durchlesen", in jedem einzelnen Fall könnte fast einen Unterschied machen 1-1 Lösung, ich weiß es. Das beste Schlagwort in Orakel ist "Probier es aus und untersuche die Ergebnisse" – Thomas

Antwort

1
UPDATE i 
SET i.Invoice_no = io.Invoice_no 
FROM Invoice_Payment i 
    INNER JOIN Invoice_Payment_updated io on i.Customer_id = io.cust_id 
WHERE i.Invoice_no <> iu.Invoice_no -- assuming Invoice_no cannot be NULL 

Wenn das Update zu viel Zeit in Anspruch nimmt, fügen WHILE Schleife und update TOP (10000) bis @@ROWCOUNT = 0. Batch-Modus kann die Leistung verbessern.

Verwandte Themen