2016-10-07 1 views
0

Die Datenbank ist Postgres, aber jede SQL-Logik sollte helfen.SQL Filtern von doppelten Zeilen wegen schlechter ETL

Ich erhalte die Menge von Verkaufsangeboten, die ein bestimmtes Produkt in der Stückliste enthalten. Ich mache das in zwei Schritten: Schritt 1, rufen Sie alle DISTINCT-Angebotsnummern ab, die ein bestimmtes Produkt (nach Produktnummer) enthalten.

Der zweite Schritt, rufen Sie das vollständige Angebot mit allen Produkten für jede eindeutige Angebotsnummer aufgeführt.

So weit, so gut. Jetzt das schwierige Stück. Einige Zeilen sind Duplikate, andere nicht. Diejenigen, die Duplikate sind (Angebotsnummer & Angebotsversion & Leitungsnummer), können oder müssen möglicherweise keine Wartung für sie haben. Ich möchte die Zeile auswählen, die Wartung hat, die größer als 0 ist. Die doppelten Zeilen, die ich ausschließen möchte, sind die, die eine 0 Wartung haben. Das Problem ist, dass einige Zeilen, die keine Duplikate haben, 0 Wartung haben, so dass ich nicht nur nach Wartung filtern kann.

Um dies spannend zu machen, enthält die Datenbank Zitate aus mehr als 20 Jahren. Und die Daten Wissenschaftler Jungs haben gerade zugegeben, dass vielleicht der ETL-Prozess einige Fehler hat ...

--- step 0 
--- cleanup the workspace 
SET CLIENT_ENCODING TO 'UTF8'; 
DROP TABLE IF EXISTS product_quotes; 

--- step 1 
--- get list of Product Quotes 
CREATE TEMPORARY TABLE product_quotes AS (
    SELECT DISTINCT master_quote_number 
    FROM w_quote_line_d 

    WHERE item_number IN (<< model numbers >>) 
); 

--- step 2 
--- Now join on that list 
SELECT 
d.quote_line_number, 
d.item_number, 
d.item_description, 
d.item_quantity, 
d.unit_of_measure, 
f.ref_list_price_amount, 
f.quote_amount_entered, 
f.negtd_discount, 
--- need to calculate discount rate based on list price and negtd discount (%) 
CASE 
    WHEN ref_list_price_amount > 0 
     THEN 100 - (ref_list_price_amount + negtd_discount)/ref_list_price_amount *100 
    ELSE 0 
END AS discount_percent, 

f.warranty_months, 
f.master_quote_number, 
f.quote_version_number, 
f.maintenance_months, 
f.territory_wid, 
f.district_wid, 
f.sales_rep_wid, 
f.sales_organization_wid, 
f.install_at_customer_wid, 
f.ship_to_customer_wid, 
f.bill_to_customer_wid, 
f.sold_to_customer_wid, 
d.net_value, 
d.deal_score, 
f.transaction_date, 
f.reporting_date 

FROM w_quote_line_d d 
INNER JOIN product_quotes pq ON (pq.master_quote_number = d.master_quote_number) 

INNER JOIN w_quote_f f ON 
    (f.quote_line_number = d.quote_line_number 
    AND f.master_quote_number = d.master_quote_number 
    AND f.quote_version_number = d.quote_version_number) 

WHERE d.net_value >= 0 AND item_quantity > 0 
ORDER BY f.master_quote_number, f.quote_version_number, d.quote_line_number 

Die Logik die doppelten Zeilen zu filtern ist wie folgt: Für jeden master_quote_number/version_number Paar, Scheck um zu sehen, ob es doppelte Zeilennummern gibt. Wenn ja, wählen Sie die mit Wartung> 0.

Selbst in einer CASE-Anweisung bin ich nicht sicher, wie man das schreibt.

Gedanken? Die Datenbank ist Postgres, aber jede SQL-Logik sollte helfen.

Antwort

0

Ich denke, Sie wollen Window Functions verwenden. Sie sind, mit einem Wort, großartig.

Hier ist eine Abfrage, die „dedupe“ auf Kriterien basieren würde:

select * 
from (
    select 
     * -- simplifying here to show the important parts 
     ,row_number() over (
      partition by master_quote_number, version_number 
      order by maintenance desc) as seqnum 
    from w_quote_line_d d 
    inner join product_quotes pq 
    on (pq.master_quote_number = d.master_quote_number) 
    inner join w_quote_f f 
    on (f.quote_line_number = d.quote_line_number 
     and f.master_quote_number = d.master_quote_number 
     and f.quote_version_number = d.quote_version_number) 
) x 
where seqnum = 1 

Die Verwendung von row_number() und den gewählten partition by und order by Kriterien gewährleisten, dass nur eine Zeile für jede Kombination von quote_number/version_number bekommen der Wert von 1, und es wird derjenige mit dem höchsten Wert in der Wartung sein (wenn Ihre Kollegen recht haben, würde es nur eins mit einem Wert> 0 irgendwie geben).

-1

Ich bin mir nicht sicher, aber vielleicht könnten Sie Group By alle anderen Spalten und MAX(Maintenance) verwenden, um nur die größten zu bekommen.

Was denkst du?

0

Können Sie etwas tun, wie ...

select 
    * 
from 
    w_quote_line_d d 
    inner join 
     (
      select 
       ... 
       ,max(maintenance) 
      from 
       w_quote_line_d 
      group by 
       ... 
     ) d1 
    on 
     d1.id = d.id 
     and d1.maintenance = d.maintenance; 

Bin ich dein Problem richtig zu verstehen?

Bearbeiten: Die Gruppe durch vergessen!

+0

Dies funktioniert, aber ein Self-Join ist fast immer teurer als die entsprechende Abfrage mit Fensterfunktionen – SlimsGhost