2016-04-14 7 views
1

Ich habe eine große Datenbank mit Verbindungen zwischen den Städten. Jede Verbindung hat eine Start- und Zielstadt, ein Startdatum und einen Preis für diese Verbindung.Wie werden Permutationen mit postgresql berechnet?

Ich würde gerne alle Kombinationen von ausgehenden + Return-Verbindungen, für alle Verbindungen und für Daten, bei denen die Rückkehr Verbindung zwischen 1-20 Tagen ist, berechnen. Wählen Sie dann den besten Preis für jede Datumskombination aus.

Beispiel:

Tabelle:

city_start,  city_end, date_start,  price 
Hamburg   Berlin  01.01.2016  100.00 
Berlin   Hamburg  10.01.2016  112.00 
Berlin   Hamburg  10.01.2016  70.00 
Berlin   Hamburg  12.01.2016  50.00 
Berlin   Hamburg  30.02.2016  20.00 
Paris   Madrid  ... 
Madrid   Paris 
London   Paris 

Wunschergebnis:

Hamburg-Berlin-Hamburg, 01.01.2016, 10.01.2016, 170.00 (100+70) 
Hamburg-Berlin-Hamburg, 01.01.2016, 12.01.2016, 150.00 (100+50) 
... 
(not Berlin-Hamburg on 30.02.2016 because it's >20 days from departure drive) 
(not London-Paris, as there is no return Paris-London) 

Ich kann die möglichen Kombinationen von erhalten:

SELECT DISTINCT city_start, city_end, city_end, city_start from table 

Aber wie kann ich jetzt berechnen ihre Permutation ns?

Antwort

2

Die Abfrage alle Paare bekommen verwendet ein join:

select tto.city_start, tto.city_end, tto.date_start, tfrom.date_end, 
     (tto.price + tfrom.price) as price 
from t tto join 
    t tfrom 
    on tto.city_end = tfrom.city_start and 
     tto.city_start = tfrom.city_end and 
     tfrom.date_start >= tto.date_start + interval '1 day' and 
     tfrom.date_end <= tto.date_start + interval '20 day'; 

den günstigsten Preis zu erhalten, Fensterfunktionen verwenden:

select tt.* 
from (select tto.city_start, tto.city_end, tto.date_start, tfrom.date_end, 
      (tto.price + tfrom.price) as price, 
      row_number() over (partition by tto.city_start, tto.city_end order by (tto.price + tfrom.price) asc) as seqnum 
     from t tto join 
      t tfrom 
      on tto.city_end = tfrom.city_start and 
       tto.city_start = tfrom.city_end and 
       tfrom.date_start >= tto.date_start + interval '1 day' and 
       tfrom.date_end <= tto.date_start + interval '20 day' 
    ) tt 
where seqnum = 1; 
+0

Großartig, das scheint im Allgemeinen zu funktionieren. Gibt es eine Alternative zum Partitionsteil row_number? (Weil die Leistung von windowAggr ziemlich schlecht ist)? – membersound

1

Hier ist eine Lösung ohne die row_number Trennteil:

SELECT 
    a.city_start, a.city_end, b.city_end, a.date_start, b.date_start, 
    min(a.price + b.price) 
FROM 
    flight AS a 
    JOIN 
    flight AS b ON a.city_start = b.city_end AND a.city_end = b.city_start 
WHERE b.date_start BETWEEN a.date_start + 1 AND a.date_start + 20 
GROUP BY a.city_start, a.city_end, b.city_end, a.date_start, b.date_start; 
+0

Das ist auch toll, danke. Eine Frage: Was ist, wenn ich eine Eigenschaft außerhalb der 'Gruppe von 'auswählen möchte? ZB 'a.carName'? – membersound

+0

Ich bin mir nicht sicher, weil ich carName nicht in den Daten sehe. Ich müsste das im Rahmen der restlichen Daten sehen um die Frage beantworten zu können ... –

+0

Wenn natürlich die Frage nur ein Beispiel zeigt. Nehmen wir an, jede Zeile hat eine zusätzliche Spalte namens "carName". Die Frage ist: wenn nicht innerhalb der Gruppe von, wie kann ich dann diesen Wert wählen – membersound

0

Wenn Sie weitere Spalten hinzufügen möchten, versuchen Sie Folgendes:

SELECT 
    a.city_start, a.city_end, b.city_end, a.date_start, b.date_start, 
    a.price + b.price, a.car_name, b.car_name 
FROM 
    flight AS a 
    JOIN 
    flight AS b ON a.city_start = b.city_end AND a.city_end = b.city_start 
    LEFT JOIN 
    flight AS c ON 
     a.city_start = c.city_start 
     AND 
     a.city_end = c.city_end 
     AND 
     a.date_start = c.date_start 
     AND (
      a.price > c.price 
      OR (
       a.price = c.price 
       AND 
       a.id > c.id)) 
    LEFT JOIN 
    flight AS d ON 
     b.city_start = d.city_start 
     AND 
     b.city_end = d.city_end 
     AND 
     b.date_start = d.date_start 
     AND (
      b.price > d.price 
      OR (
       b.price = d.price 
       AND 
       b.id > d.id)) 
WHERE 
    b.date_start BETWEEN a.date_start + 1 AND a.date_start + 20 
    AND 
    c.id IS NULL 
    AND 
    d.id IS NULL; 
Verwandte Themen