2009-11-02 9 views
5

Ich habe eine Tabelle, die eine Liste von Spielen ist, die in einer SQLite3-Datenbank gespielt wurden. Das Feld "datetime" ist das Datum, an dem das Spiel beendet wurde. Das Feld "Dauer" ist die Anzahl der Sekunden, die das Spiel dauerte. Ich möchte wissen, wie viel Prozent der letzten 24 Stunden mindestens 5 Spiele gleichzeitig ausgeführt haben. Ich finde heraus, zu sagen, wie viele Spiele zu einem bestimmten Zeitpunkt ausgeführt werden:Sqlite3: Need to Cartesian Am Datum

select count(*) 
from games 
where strftime('%s',datetime)+0 >= 1257173442 and 
     strftime('%s',datetime)-duration <= 1257173442 

Wenn ich einen Tisch habe, die einfach eine Liste mit jeder Sekunde war (oder alle 30 Sekunden oder so) ich ein absichtliches cartisian Produkt tun könnte dies wie:

select count(*) 
from (
    select count(*) as concurrent, d.second 
    from games g, date d 
    where strftime('%s',datetime)+0 >= d.second and 
     strftime('%s',datetime)-duration <= d.second and 
     d.second >= strftime('%s','now') - 24*60*60 and 
     d.second <= strftime('%s','now') 
    group by d.second) x 
where concurrent >=5 

gibt es eine Möglichkeit zu dieses Datum Tabelle auf den fly erstellen? Oder dass ich einen ähnlichen Effekt bekommen kann, ohne tatsächlich eine neue Tabelle erstellen zu müssen, die einfach eine Liste aller Sekunden dieser Woche ist?

Dank

Antwort

2

Große Frage!

Hier ist eine Abfrage, die ich denke gibt Ihnen, was Sie wollen, ohne eine separate Tabelle zu verwenden. Beachten Sie, dass dies ungetestet ist (also wahrscheinlich Fehler enthält) und ich habe angenommen, dass datetime eine int-Spalte mit # Sekunden ist, um eine Tonne Strftime zu vermeiden.

select sum(concurrent_period) from (
    select min(end_table.datetime - begin_table.begin_time) as concurrent_period 
    from (
    select g1.datetime, g1.num_end, count(*) as concurrent 
    from (
     select datetime, count(*) as num_end 
      from games group by datetime 
    ) g1, games g2 
    where g2.datetime >= g1.datetime and 
      g2.datetime-g2.duration < g1.datetime and 
      g1.datetime >= strftime('%s','now') - 24*60*60 and 
      g1.datetime <= strftime('%s','now')+0 
) end_table, (
    select g3.begin_time, g1.num_begin, count(*) as concurrent 
    from (
     select datetime-duration as begin_time, 
      count(*) as num_begin 
      from games group by datetime-duration 
    ) g3, games g4 
    where g4.datetime >= g3.begin_time and 
      g4.datetime-g4.duration < g3.begin_time and 
      g3.begin_time >= strftime('%s','now') - 24*60*60 and 
      g3.begin_time >= strftime('%s','now')+0 
) begin_table 
    where end_table.datetime > begin_table.begin_time 
     and begin_table.concurrent < 5 
     and begin_table.concurrent+begin_table.num_begin >= 5 
     and end_table.concurrent >= 5 
     and end_table.concurrent-end_table.num_end < 5 
    group by begin_table.begin_time 
) aah 

Die Grundidee ist, zwei Tabellen zu machen: eine mit der Anzahl der gleichzeitigen Spiele zu der Zeit jedes Spiel beginnen, und eine mit der Anzahl der gleichzeitigen Spiele in der Endzeit. Dann nimm die Tische zusammen und nimm nur Reihen an "kritischen Punkten", wo # von gleichzeitigen Spielen 5 kreuzt. Für jede kritische Anfangszeit nimm die kritische Endzeit, die am ehesten passiert und die hoffentlich alle Perioden gibt, in denen mindestens 5 Spiele liefen gleichzeitig.

Hoffe, das ist nicht zu kompliziert, um hilfreich zu sein!

0

Warum Sie das Datum und halten nur die Zeit nicht schneiden, wenn Sie Ihre Daten für ein bestimmtes Datum filtern jedes Mal einzigartig ist. Auf diese Weise benötigen Sie nur eine Tabelle mit Zahlen von 1 bis 86400 (oder weniger, wenn Sie größere Intervalle verwenden), Sie können zwei Spalten erstellen, "von" und "bis", um die Intervalle zu definieren. Ich bin nicht vertraut mit SQLite-Funktionen, aber nach dem Handbuch müssen Sie die Strftime-Funktion mit diesem Format verwenden: HH: MM: SS.

3

Erstens kann ich mir keinen Weg vorstellen, wie Sie Ihr Problem angehen können, indem Sie spontan oder ohne Hilfe einer zusätzlichen Tabelle einen Tisch erstellen. Es tut uns leid.

Mein Vorschlag ist, dass Sie sich auf eine statische Zahlentabelle verlassen.

Erstellen einen feststehenden Tisch mit dem Format:

CREATE TABLE Numbers (
    number INTEGER PRIMARY KEY 
); 

mit der Anzahl von Sekunden in 24h füllt ihn (24 * 60 * 60 = 84600). Ich würde jede Skriptsprache verwenden zu tun, dass das Insert-Anweisung:

insert into numbers default values; 

nun die Zahlen Tabelle sind die Zahlen von 1 bis 84600. Ihre Anfrage hat, wird sie geändert werden, um zu sein:

select count(*) 
    from (
     select count(*) as concurrent, strftime('%s','now') - 84601 + n.number second 
      from games g, numbers n 
     where strftime('%s',datetime)+0 >= strftime('%s','now') - 84601 + n.number and 
       strftime('%s',datetime)-duration <= strftime('%s','now') - 84601 + n.number 
     group by second) x 
where concurrent >=5 

Without a Verfahrenssprache in der Mischung, das ist das Beste, was Sie tun können, denke ich.

+0

Die Zahlen Tabelle Trick ist üblich, wenn Sie nicht rekursive CTE/Unterabfrage Factoring verwenden können ... –

+0

Ja, und die Zahlen Tabelle wird in mehr als diesem speziellen Szenario nützlich sein. Sie können es immer beschränken und erhalten nur die Teilmenge, die Sie brauchen: 'Wählen Sie die Nummer aus den Zahlen Limit 100;' Es ist nur ein bequemes Konstrukt zu haben, um ... – cethegeek

2

Kevin prügelt mich eher zur Pointe (+1), aber ich werde diese Variante posten, da sie sich zumindest ein wenig unterscheidet.

Die wichtigsten Ideen sind

  • die Daten Karte in einen Strom von Ereignissen mit Attributen Zeit und ‚Polarität‘ (= Start oder Ende des Spiels)
  • Halten Sie eine laufende Summe davon, wie viele Spiele sind Öffnen Sie zum Zeitpunkt jedes Ereignisses (dies erfolgt durch Bildung eines Self-Join auf dem Ereignisstream)
  • Finden Sie die Ereignis Zeiten, wo die Anzahl der Spiele (wie Kevin sagt) übergänge bis 5 oder bis zu 4
  • Ein kleiner Trick: Addieren Sie alle bis zu 4 Mal und nehmen Sie t weg er up-to-5 - die Reihenfolge ist nicht wichtig
  • Das Ergebnis die Anzahl der Sekunden mit 5 oder mehr Spiele offen

Ich habe keine SQLLite ausgegeben ist, also habe ich mit MySQL getestet und ich habe mir nicht die Mühe gemacht, das Zeitfenster zu begrenzen, um geistig gesund zu bleiben. Sollte nicht schwer zu revidieren sein.

Auch, und was noch wichtiger ist, habe ich nicht überlegt, was zu tun ist, wenn Spiele am Anfang oder Ende der Periode geöffnet sind!

Etwas sagt mir, dass es hier eine große Vereinfachung gibt, aber ich habe es noch nicht entdeckt.

SELECT SUM(event_time) 
FROM (
SELECT -ga.event_type * ga.event_time AS event_time, 
    SUM( ga.event_type * gb.event_type) event_type 
FROM 
    (SELECT UNIX_TIMESTAMP(g1.endtime - g1.duration) AS event_time 
      , 1 event_type 
     FROM games g1 
     UNION 
     SELECT UNIX_TIMESTAMP(g1.endtime) 
      , -1 
     FROM games g1) AS ga, 
    (SELECT UNIX_TIMESTAMP(g1.endtime - g1.duration) AS event_time 
      , 1 event_type 
     FROM games g1 
     UNION 
     SELECT UNIX_TIMESTAMP(g1.endtime) 
      , -1 
     FROM games g1) AS gb 
WHERE 
    ga.event_time >= gb.event_time 
GROUP BY ga.event_time 
HAVING SUM(ga.event_type * gb.event_type) IN (-4, 5) 
) AS gr