Berechnung der Anzahl der gleichzeitigen Ereignisse in SQL
Ich habe eine Tabelle, die Sie hält anrufen, mit den folgenden Feldern:
- ID
- STARTTIME
- ENDTIME
- STATUS
- CALL_FROM
- CALL_TO
Gibt es 2,9 Millionen Datensätze werden in eine lokale PostgreSQL-Datenbank. Ich fügte hinzu, Indizes-ID (unique index), starttime und endtime.
Suche auf stackoverflow fand ich einige nützliche SQL so geändert, daß, was ich denke logisch funktionieren sollte. Das problem ist, dass die Abfrage läuft für viele Stunden und kehrt nie zurück:
SELECT T1.sid, count(*) as CountSimultaneous
FROM calls_nov T1, calls_nov T2
WHERE
T1.StartTime between T2.StartTime and T2.EndTime
and T1.StartTime between '2011-11-02' and '2011-11-03'
GROUP BY
T1.sid
ORDER BY CountSimultaneous DESC;
Kann mir bitte jemand entweder einen Weg vorschlagen, um fix die Abfrage/index, so dass es tatsächlich funktioniert, oder schlagen einen anderen Weg zur Berechnung der gleichzeitigen Anrufe?
EDIT:
Erklären, plan:
Sort (cost=11796758237.81..11796758679.47 rows=176663 width=35)
Sort Key: (count(*))
-> GroupAggregate (cost=0.00..11796738007.56 rows=176663 width=35)
-> Nested Loop (cost=0.00..11511290152.45 rows=57089217697 width=35)
Skript zur Erstellung der Tabelle:
CREATE TABLE calls_nov (
sid varchar,
starttime timestamp,
endtime timestamp,
call_to varchar,
call_from varchar,
status varchar);
Index-Erstellung:
CREATE UNIQUE INDEX sid_unique_index on calls_nov (sid);
CREATE INDEX starttime_index on calls_nov (starttime);
CREATE INDEX endtime_index on calls_nov (endtime);
- T1 und T2 sind die gleichen??
- Können Sie uns das erklären-plan? postgresql.org/docs/8.1/static/sql-explain.html Auch, vorausgesetzt, dass "sid" ist die ID, wie es in der und wählen Sie die Gruppierung, indem es nicht sinnvoll ist, die "zählen" würde immer 1.
- Natürlich sind Sie das...es ist ein Protokoll der Aufrufe. Er möchte wissen, wie viele gleichzeitige Anrufe waren auch geschieht, bei jedem Anruf.
- Was
t1.sid
? - SID ist die eindeutige ID von jedem Anruf.
- In diesem Fall, was passiert, wenn Sie tun: SELECT count(*) as CountSimultaneous VON calls_nov T1, calls_nov T2 WHERE T1.Startzeit zwischen T2.StartTime und T2.EndTime und T1.Startzeit zwischen '2011-11-02' und '2011-11-03'
- dies ist das Ergebnis von explain plan " - Aggregats (Kosten=11144150221.35..11144150221.36 rows=1 width=0)"
- Sieht aus wie es ist nicht mit der Indizes - können Sie auch die Skripts zur Erstellung der Tabelle?
- Hinzugefügt create table-und index-scripts. Danke!!!
- Ich würde versuchen, einen zusammengesetzten index auf start-und end-Zeit - sieht aus wie es ' s nicht mit den einzelnen Tasten.
- Bitte verwenden Sie keine implizite join-syntax (Komma-getrennte Liste nach
FROM
), da es einfach zu einfach, um in cross-joins, oder andere Dinge (ich glaube, es kann out-of-standard, jetzt, aber auch für rückwärts-Kompatibilität). Verwenden Sie immer explizite syntax, wie in @Eric ' s Antwort. - einigten sich auf die implizite joins. Auch Hinzugefügt-index für starttime, endtime, aber keinen performance-Gewinn.
InformationsquelleAutor Sologoub | 2012-01-04
Schreibe einen Kommentar Antworten abbrechen
Du musst angemeldet sein, um einen Kommentar abzugeben.
1.) Die Abfrage hat nicht fangen alle überlappungen - dies wurde behoben, indem die anderen Antworten schon.
2.) Der Datentyp der Spalten
starttime
undendtime
isttimestamp
. So IhrWHERE
- Klausel ist leicht falsch, zu:Dazu gehören '2011-11-03 00:00'. Die Obere Grenze muss ausgeschlossen.
3.) Entfernt den gemischten Fall syntax ohne Anführungszeichen. Nicht gequotete Identifizierer sind gegossen, um den unteren Fall automatisch. Um es einfach auszudrücken: am Besten nicht verwenden Sie groß-und Kleinschreibung von Bezeichnern in allen in PostgreSQL.
4.) Verwandelt sich die Abfrage auf die explizite JOIN-das ist immer vorzuziehen. Tatsächlich, ich habe es ein LEFT [OUTER] JOIN, weil ich will, zu zählen, fordert, dass keine überschneidungen mit anderen Aufrufe, zu.
5.) Vereinfacht die syntax ein bisschen zum erreichen dieses base-Abfrage:
Diese Abfrage ist extrem langsam für einen großen Tisch, weil jede Zeile ab '2011-11-02' verglichen werden, um jede Zeile die gesamte Tabelle, die führt zu (fast) O(n2) Kosten.
Schneller
Können wir drastisch reduzieren die Kosten durch der Vorauswahl der möglichen Kandidaten. Wählen Sie nur die Spalten und Zeilen Sie benötigen. Ich mache das mit zwei CTE.
x
y
)x
. -> CTEy
Schneller noch
Ich habe ein real life table von 350.000 Zeilen mit überlappenden Beginn /Ende-Zeitstempel gleicht. Ich verwendet, die für eine quick benchmark. PostgreSQL 8.4, knappe Ressourcen, weil es eine test-DB. Indizes auf
start
undend
. (Index auf die Spalte ID ist hier irrelevant.) Getestet mitEXPLAIN ANALYZE
, best of 5.Gesamt-Laufzeit: 476994.774 ms
CTE Variante:
Gesamt-Laufzeit: 4199.788 ms-das ist > den Faktor 100.
Nach dem hinzufügen eines mehrspaltigen index der form:
Gesamt-Laufzeit: 4159.367 ms
Ultimate Speed
Wenn das nicht genug ist, gibt es eine Möglichkeit es zu beschleunigen noch eine andere Größenordnung. Anstelle des CTEs oben, materialisieren die temp-Tabellen und - das ist der entscheidende Punkt - erstellen Sie eine index auf den zweiten. Könnte so Aussehen:
Ausführen als einer Transaktion:
Lesen über temporäre Tabellen in der Anleitung.
Ultimative Lösung
Erstellen Sie eine plpgsql-Funktion, welches die Magie.
Diagnostizieren, die typische Größe des temp-Tabellen. Erstellen Sie eigenständige und Messen:
Wenn Sie größer sind als Ihre Einstellung für temp_buffers dann vorübergehend legen Sie Sie hoch genug, in Ihrer Funktion zu halten, sowohl Ihre temporären Tabellen im RAM. Es ist eine große Beschleunigung, die, wenn Sie nicht haben, um die swap disc. (Kann, muss zuerst die Verwendung von temporären Tabellen in der Sitzung wirksam sind.)
Nennen:
Gesamt-Laufzeit: 138.169 ms-das ist Faktor 3000
Was kann man tun, um ihn zu beschleunigen?
Allgemeine performance-Optimierung.
varchar
. Feste meine Antwort auch. Ich bin sehr neugierig, wenn Sie bekommen einen ähnlichen speedup? BTW, die Anpassungtemp_buffers
ist wahrscheinlich nicht nötig, wenn deine db config hat sane Parameter. Die temp-Tabellen sollten nicht so groß ist. (Aber testen >> raten)Hier ist, was die möglichen überschneidungen Aussehen, wo " A " ist die "Referenz" - Intervall. Beachten Sie, dass die Abfrage unten (ganz weit unten) nicht das gleiche Ergebnis wie eine der Antworten noch nicht gepostet.
"B" überlappt "Ein" an alle. "C" stößt es. {"D", "E", "F", "G"} überlappt. "H" anliegt, es. "Ich" überlappt es überhaupt nicht.
Sehen Sie alle überlappenden Intervallen wie diese. (Ich habe gerade verwendet to_char() machen es einfach, zu sehen alle Daten. Sie können es weglassen in der Produktion.)
Können Sie sehen aus dieser Tabelle, dass der "Eine" sollte zählen 5, einschließlich sich selbst. "B" sollte count-1; es überlappt sich selbst, aber keine anderen Intervalle überlappen. Das scheint die richtige Sache zu tun.
Zählen ist einfach, aber läuft wie ein gerissener Schildkröte. Das ist, weil die Bewertung einer überlappung nimmt eine Menge Arbeit.
Um bessere Leistung zu erhalten, können Sie die "Tabelle" oben in einer common table expression, und die Zählung basiert auf , dass.
Ich nehme an, Sie wollen wissen, die Menge der aktiven Anrufe zu einem bestimmten Zeitpunkt. Andere Antworten geben Sie, wie viele andere Anrufe wurden aktiv, während der aktuelle Anruf aktiv war. Für sehr lange fordert, dies können Ihnen sehr hohe zahlen. Es wurde mir angedeutet, dass die Anzahl der aktiven Anrufe, was Sie wollte, von einem Ihrer Kommentare zu den anderen Antworten (außerdem arbeite ich auch in der Telekommunikation). Leider habe ich nicht genug Ruf zu kommentieren, die Antwort noch, als ich mein Konto erstellt um diese Frage zu beantworten. Um die Anzahl der aktiven Anrufe, könntest du eine variable um eins erhöht, wenn ein Anruf gestartet wird, und verringert sich um eins, wenn es endete. Ich habe getestet, die auf eine MySQL-Datenbank mit über 50 Millionen Aufrufe. Sorry wegen der syntax Unterschiede zwischen MySQL und pgsql.
Fügte ich temporäre Tabellen für die Geschwindigkeit, aber mit nur 2m Zeilen und Indizes, die Sie möglicherweise nicht erforderlich. MySQL kann nicht auf die gleiche temporäre Tabelle doppelt, so hatte ich zwei.
Den inneren SELECT gibt zwei Spalten. Die Spalte Zeit enthält jede Startzeit und jede EndTime aus der ursprünglichen Tabelle (zweimal die Menge der Zeilen), und die delta-Spalte ist +1 oder -1, je nachdem, welche Spalte wurde in 'die Zeit'. Dieses set ist bestellt durch die Zeit, die wir dann Durchlaufen, die in der äußeren SELECT.
Anstelle von", UM DURCH gleichzeitige DESC" wie, Sie hatten in Ihrer Anfrage, ich würde den Einsatz eines zusätzlichen äußeren AUSWÄHLEN, wo ich Sie bekommen könnte MAX, MIN etc. Werte und ich konnte auch GROUP BY Datum, Stunde usw. Dieser Teil der Abfrage (ORDER BY Auger DESC), eigentlich wollte ich das nicht testen. Ich habe meinen eigenen Vorschlag mit einer zusätzlichen äußeren Abfrage, wie UM VON sich nicht wie erwartet in MySQL bei der Bestellung durch eine variable gesetzt wurde, in der gleichen WÄHLEN. Er befiehlt, indem der Vorherige Wert der Variablen statt. Wenn Sie unbedingt müssen, um durch gleichzeitige Anrufe (und pgsql hat das gleiche problem), ich glaube, Sie könnten dies umgehen, indem Sie erneut mit Hilfe einer zusätzlichen äußeren AUSWÄHLEN und bestellen dort.
Die Abfrage, die ich lief war sehr schnell! Es durchsucht jede temporäre Tabelle, die einmal, und dann die Kombination der beiden einst (mit weniger Daten pro Zeile), und für meine eigene version mit einer zusätzlichen äußeren Abfrage durchsucht werden durch die Kombination noch einmal und dann Gruppen. Jeder Tisch wird nur einmal gescannt! Dies wird alles im RAM wenn Ihre hardware-Konfiguration und ermöglicht es. Andere Antworten (oder Fragen) wird Ihnen helfen, wenn es nicht.
Versuchen Sie, diese anstelle Ihrer zwischen-und einen cross join:
and t1.sid != t2.sid
um die gleiche Zeile nicht mit sich selbst verknüpftleft join
, undcount(t2.sid)
, und es würde nur geben Sie 1 weniger für jede Zahl. Oder Sie tun könnencount(1)-1
. Wahrscheinlich waschen oder so.