2010-01-22 8 views
5

Edit 1 (Erklärung): Vielen Dank für die Antworten bis jetzt! Die Antwort ist erfreulich.
Ich möchte die Frage ein wenig klären, weil ich basierend auf den Antworten einen Aspekt des Problems nicht korrekt beschrieben habe (und ich bin mir sicher, dass das meine Schuld ist, da ich Schwierigkeiten hatte, es selbst für mich zu definieren).
Hier ist der Haken: Die Ergebnismenge sollte NUR die Datensätze mit TSTAMP BETWEEN "2010-01-03" UND "2010-01-09" UND einen Datensatz enthalten, wo der Tstamp NULL für jede Bestellnummer in der ersten ist set (es wird immer mit null tstamp für jede order_num sein).
Die bisher gegebenen Antworten scheinen alle Datensätze für eine bestimmte order_num, wenn es any mit tstamp BETWEEN '2010-01-03' UND '2010-01-09'. Wenn zum Beispiel ein anderer Datensatz mit order_num = 2 und tstamp = 2010-01-12 00:00:00 vorhanden ist, sollte nicht im Ergebnis enthalten sein.Effizienter SQL als mit "A UNION (B in A)"?

Original Frage:
Betrachten Sie eine Auftragstabelle enthält ID (eindeutige), order_num, tstamp (ein Zeitstempel) und ITEM_ID (die einzelnen Artikel in einer Bestellung enthalten). Tstamp ist Null, es sei denn, die Reihenfolge wurde geändert. In diesem Fall gibt es einen anderen Datensatz mit identischer Bestellnummer und Tstamp enthält dann den Zeitstempel der Änderung.

Beispiel ...

 
id order_num tstamp    item_id 
__ _________ ___________________ _______ 
0   1       100 
1   2       101 
2   2 2010-01-05 12:34:56  102 
3   3       113 
4   4       124 
5   5       135 
6   5 2010-01-07 01:23:45  136 
7   5 2010-01-07 02:46:00  137 
8   6       100 
9   6 2010-01-13 08:33:55  105 

Was ist die effizienteste SQL-Anweisung alle Aufträge abgerufen werden (basierend auf order_num), die während eines bestimmten Zeitraums ein oder mehrmals geändert wurden? Mit anderen Worten, für jede Reihenfolge benötigen wir alle Datensätze mit der gleichen Ordnungsnummer (einschließlich der mit NULL Tstamp), für jede Ordnungsnummer WHERE mindestens eine der Ordnungsnummern hat Tstamp NOT NULL und Tstamp BETWEEN '2010-01-03' UND '2010-01-09'. Es ist die "WHERE mindestens einer der OrderNum hat Tstamp NOT NULL", mit denen ich Schwierigkeiten habe.

Die Ergebnismenge soll wie folgt aussehen:

 
id order_num tstamp    item_id 
__ _________ ___________________ _______ 
1   2       101 
2   2 2010-01-05 12:34:56  102 
5   5       135 
6   5 2010-01-07 01:23:45  136 
7   5 2010-01-07 02:46:00  137 

Die SQL, die ich herauskommen, ist dies, die im Wesentlichen „A UNION (B in A)“, aber es führt langsam und ich hoffe, es ist eine effizientere Lösung:

 
SELECT history_orders.order_id, history_orders.tstamp, history_orders.item_id 
FROM 
    (SELECT orders.order_id, orders.tstamp, orders.item_id 
    FROM orders 
    WHERE orders.tstamp BETWEEN '2010-01-03' AND '2010-01-09') 
    AS history_orders 
UNION 
SELECT current_orders.order_id, current_orders.tstamp, current_orders.item_id 
FROM 
    (SELECT orders.order_id, orders.tstamp, orders.item_id 
    FROM orders 
    WHERE orders.tstamp IS NULL) 
    AS current_orders 
WHERE current_orders.order_id IN 
    (SELECT orders.order_id 
    FROM orders 
    WHERE orders.tstamp BETWEEN '2010-01-03' AND '2010-01-09'); 
+0

Ich bin neugierig auf die Leistung der bereitgestellten Abfragen, vielleicht könnten Sie Testergebnisse teilen? –

+0

Ich werde in Kürze über die Leistungsverbesserung der endgültigen Lösung berichten - es ist signifikant. – machinatus

Antwort

0

Nochmals vielen Dank für alle Vorschläge. Ich habe drei Lösungen gefunden, die funktionieren, einschließlich meines Originals. Am Ende habe ich einige Leistungsergebnisse hinzugefügt, die nicht so gut sind, wie ich es mir erhofft hatte. Wenn sich jemand verbessern kann, wäre ich begeistert!

1) Die beste Lösung gefunden, so weit zu sein scheint:

 
SELECT history_orders.order_id, history_orders.tstamp, history_orders.item_id 
FROM 
    (SELECT orders.order_id, orders.tstamp, orders.item_id 
    FROM orders 
    WHERE orders.tstamp BETWEEN '2010-01-03' AND '2010-01-09' 
    OR orders.tstamp IS NULL) 
    AS history_orders 
WHERE history_orders.order_id IN 
    (SELECT orders.order_id 
    FROM orders 
    WHERE orders.tstamp BETWEEN '2010-01-03' AND '2010-01-09'); 

2) Ich habe auch versucht EXISTS anstelle von IN verwenden, die eine zusätzliche erfordert WHERE-Klausel in der letzten SELECT:

 
SELECT history_orders.order_id, history_orders.tstamp, history_orders.item_id 
FROM 
    (SELECT orders.order_id, orders.tstamp, orders.item_id 
    FROM orders 
    WHERE orders.tstamp BETWEEN '2010-01-03' AND '2010-01-09' 
    OR orders.tstamp IS NULL) 
    AS history_orders 
WHERE EXISTS 
    (SELECT orders.order_id 
    FROM orders 
    WHERE history_orders.order_id = orders.order_id 
    AND orders.tstamp BETWEEN '2010-01-03' AND '2010-01-09'); 

3) Und schließlich gibt es meine ursprüngliche Lösung, mit UNION.

Kommentare:
Um die Tabellengröße zu kommentieren, beinhaltet mein tatsächliches "reales Welt" Problem 4 Tabellen (verbunden mit inneren Verbindungen), die 98, 2189, 43897, 785656 Aufzeichnungen enthalten.

Leistung - lief ich jede Lösung dreimal und hier sind meine wirkliche Welt Ergebnisse:
1: 52, 51, 51 Sekunden
2: 54, 54, 53 s
3: 56, 56, 56 s

+0

Haben Sie Indizes für order_id und tstamp? –

+0

Nein, und ich kann den Entwurf nicht ändern, da dies kein Schlüsselbericht ist. Es wird nicht sehr oft laufen, also bin ich ziemlich zufrieden mit dem, was ich jetzt habe. Nicht, dass ich nicht daran interessiert wäre, Dinge weiter zu verbessern, nur für mein Wissen! – machinatus

3

vielleicht eine Unterabfrage:

select * from order o where o.order_num in (select distinct 
    order_num from order where tstamp between '2010-01-03' and '2010-01-09') 
+0

+1 für die lesbarste der ersten drei nahezu identische Lösungen. – egrunin

+0

Also, nach der Einbeziehung der Änderung notwendig auf der Grundlage meiner Klarstellung ("Edit 1") in meiner ursprünglichen Frage, ist meine Lösung, diese Antwort beim Hinzufügen von Unterabfrage zu der ersten Auswahl mit einer WHERE-Klausel, die die Ergebnisse auf tstamp ZWISCHEN 'begrenzt 2010-01-03 'AND' 2010-01-09 ' ODER tsamp NULL. Ich werde in Kürze eine vollständige und korrekte Antwort hinzufügen. – machinatus

1

Es sei denn, ich habe Misund erstood, so etwas wie dies sollte es tun:

SELECT o1.id, o1.order_num, o.tstamp, o.item_id 
FROM orders o1 
WHERE EXISTS(
    SELECT * FROM orders o2 
    WHERE o1.order_num = o2.order_num 
     AND o2.tstamp BETWEEN '2010-01-03' AND '2010-01-09') 

Vorteil der Verwendung von EXISTS ist, dass sie, sobald sie Bußgelder das erste Spiel beendet.

0

Ich hoffe, ich habe Ihre Frage richtig. Dies sollte alle Bestellungen zurückgeben, deren Bestellung innerhalb des angegebenen Zeitstempels geändert wurde.

SELECT o.order_id, o.tstamp, o.item_id 
FROM orders o 
JOIN (SELECT DISTINCT o2.order_num 
     FROM orders o2 
     WHERE o2.tstamp BETWEEN '2010-01-03' AND '2010-01-09') o3 
ON (o3.order_num = o.order_num) 
0

Sie können die Tabelle selbst verbinden.Vereinfacht würde dies aussehen:

select order_id 
from orders all_orders 
inner join orders not_null_orders 
    on all_orders.order_id = not_null_orders.order_id 
where 
    not_null_orders.tstamp is not null 
    and all_orders.tstamp between '2010-01-03' AND '2010-01-09' 
1

Ich weiß, es ist sehr spät für Repliying, aber ich sah gerade diesen Beitrag und ich dachte, vielleicht sollte ich dies einmal versuchen, wie wäre es mit dieser Abfrage, es ist wirklich sehr klein im Vergleich zu allen oben genannten Lösungen und löst den Zweck.

select * from orders_gc where order_num in 
    (select order_num 
    from orders_gc 
    group by order_num 
    having count(id) > 1 and 
    MAX(tstamp) between '03-jan-2010' and '09-jan-2010')