Frage
Am Suche eine einzelne Abfrage, die folgenden Informationen in der Tabelle konvertieren
name:time :state
a :10:00 AM:login
b :10:05 AM:login
a :10:06 AM:chatting
a :10:08 AM:Idle
b :10:11 AM:chatting
a :10:10 AM:Logout
b :10:12 AM:Logout
, um so etwas wie dies (wegen des Zeitbereiches von 10.00 bis 10.15 Uhr als Abfragezeitraum)
name: State :Duration
a : chatting :2 Minutes
a : Idle :2 Minutes
b : chatting :1 Minute
Kann dies nur unter Verwendung von SQL getan? Ist mit Informix Version 11.5
Lösung
Es kann in einer einzigen SQL-Anweisung erfolgen. Hier ist der Beweis.
Setup
CREATE TEMP TABLE eventtable
(
name CHAR(3) NOT NULL,
time DATETIME HOUR TO MINUTE NOT NULL,
state CHAR(8) NOT NULL
);
INSERT INTO eventtable(name, time, state) VALUES('a', '10:00', 'login');
INSERT INTO eventtable(name, time, state) VALUES('b', '10:05', 'login');
INSERT INTO eventtable(name, time, state) VALUES('a', '10:06', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('a', '10:08', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('b', '10:11', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('a', '10:10', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('b', '10:12', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:01', 'login');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:02', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:03', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:04', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:05', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:06', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:07', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:08', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:09', 'login');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:11', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:12', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:13', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:14', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:15', 'Logout');
Die richtige Abfrage
Beachten Sie die Bedingungen. Die Ergebnistabelle muß die Zeiträume zwischen dem ‚login‘ ausschließen und dem ersten anderen Ereignisse; ferner muss er die Zeit zwischen ‚Logout‘ und dem nächsten Ereignisse (vermutlich ein ‚login‘) auszuschließen. Die Selbstverknüpfung zwischen der Tabelle auf der namen Spalte und dann die asymmetrischen kommt auf der Zeit Spalte (mit ‚<
‘) stellt sicher, dass die Ereignisse in zeitlicher Reihenfolge sind. Die NICHT VORHANDEN Unter wählen Sie sorgt dafür, dass nur benachbarte Ereignisse betrachtet werden. Unter Verwendung zwischen und in der Unterabfrage ist ein Fehler, weil es seine Endpunkte enthält, und es ist entscheidend, dass r1.time
und r2.time
sind aus dem Bereich ausgeschlossen; es dauerte ein paar Minuten, dass die Fehler zu erkennen (die Abfrage lief, kehrte aber keine Zeilen, aber Warum ?)!
SELECT r1.name, r1.state, r2.TIME - r1.TIME AS duration
FROM eventtable r1, eventtable r2
WHERE r1.name = r2.name
AND r1.time < r2.time
AND r1.state != 'login'
AND r1.state != 'Logout'
AND r1.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
AND DATETIME(10:15) HOUR TO MINUTE
AND r2.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
AND DATETIME(10:15) HOUR TO MINUTE
AND NOT EXISTS (SELECT 1 FROM eventtable r3
WHERE r3.time > r1.time AND r3.time < r2.time
AND r3.name = r1.name
AND r3.name = r2.name);
Dies erzeugt die Antwort:
name state duration
a chatting 0:02
a Idle 0:02
b chatting 0:01
c chatting 0:01
c Idle 0:01
c Idle 0:01
c Idle 0:01
c chatting 0:01
c Idle 0:01
c chatting 0:01
c Idle 0:01
Die ‚Dauer‘ Wert ist ein Intervall Stunde zu Minute; wenn Sie einen Wert in nur wenigen Minuten wollen, müssen Sie es mit einer Besetzung umwandeln (4 für die Präzision mit bis zu 1440 Minuten Intervallen zu ermöglichen, oder 1 Tag; die Daten für einen längeren Zeitrahmen nicht eindeutig ist):
(r2.time - r1.time)::INTERVAL MINUTE(4) TO MINUTE
Oder:
CAST (r2.time - r1.time AS INTERVAL MINUTE(4) TO MINUTE)
IBM Informix Dynamic Server (IDS) hat eine sehr ausführliche Notationen für Zeitkonstanten. 00: Im Standard-SQL, können Sie Zeit wie die Art und TIME '10 verwenden 00' als Wert, aber die Sekunden würden in strengem Standard-SQL erforderlich. IDS tut genau Arten liefern, die Menschen wollen - wie DATETIME- Stunde zu Minute. Sie würden auch INTERVAL MINUTE (4) in Standard-SQL schreiben; die 'MINUTE' sollte optional sein.
Falsche Abfrage
In meinem Kommentar zu Ray Hidayat Antwort, habe ich darauf hingewiesen, dass die EXISTS Unterabfrage notwendig ist, um sicherzustellen, dass die Ereignisse unter Berücksichtigung angrenzen - sind keine dazwischenliegenden Ereignisse. Hier ist die gleiche Abfrage mit Start- und Endzeiten an den Ausgang gegeben, und die EXISTS-Klausel fehlt (und ‚Dauer‘ umbenannt zu ‚Ablauf‘):
SELECT r1.name, r1.state, r2.TIME - r1.TIME AS lapse,
r1.time AS start, r2.time AS end
FROM eventtable r1, eventtable r2
WHERE r1.name = r2.name
AND r1.time < r2.time
AND r1.state != 'login'
AND r1.state != 'Logout'
AND r1.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
AND DATETIME(10:15) HOUR TO MINUTE
AND r2.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
AND DATETIME(10:15) HOUR TO MINUTE;
Dies erzeugt die Antwort:
name state lapse start end
a chatting 0:04 10:06 10:10
a chatting 0:02 10:06 10:08
a Idle 0:02 10:08 10:10
b chatting 0:01 10:11 10:12
c chatting 0:13 10:02 10:15
c chatting 0:12 10:02 10:14
c chatting 0:11 10:02 10:13
c chatting 0:10 10:02 10:12
c chatting 0:09 10:02 10:11
c chatting 0:07 10:02 10:09
c chatting 0:06 10:02 10:08
c chatting 0:05 10:02 10:07
c chatting 0:04 10:02 10:06
c chatting 0:03 10:02 10:05
c chatting 0:02 10:02 10:04
c chatting 0:01 10:02 10:03
c Idle 0:12 10:03 10:15
c Idle 0:11 10:03 10:14
c Idle 0:10 10:03 10:13
c Idle 0:09 10:03 10:12
c Idle 0:08 10:03 10:11
c Idle 0:06 10:03 10:09
c Idle 0:05 10:03 10:08
c Idle 0:04 10:03 10:07
c Idle 0:03 10:03 10:06
c Idle 0:02 10:03 10:05
c Idle 0:01 10:03 10:04
c Idle 0:10 10:05 10:15
c Idle 0:09 10:05 10:14
c Idle 0:08 10:05 10:13
c Idle 0:07 10:05 10:12
c Idle 0:06 10:05 10:11
c Idle 0:04 10:05 10:09
c Idle 0:03 10:05 10:08
c Idle 0:02 10:05 10:07
c Idle 0:01 10:05 10:06
c Idle 0:08 10:07 10:15
c Idle 0:07 10:07 10:14
c Idle 0:06 10:07 10:13
c Idle 0:05 10:07 10:12
c Idle 0:04 10:07 10:11
c Idle 0:02 10:07 10:09
c Idle 0:01 10:07 10:08
c chatting 0:04 10:11 10:15
c chatting 0:03 10:11 10:14
c chatting 0:02 10:11 10:13
c chatting 0:01 10:11 10:12
c Idle 0:03 10:12 10:15
c Idle 0:02 10:12 10:14
c Idle 0:01 10:12 10:13
c chatting 0:02 10:13 10:15
c chatting 0:01 10:13 10:14
c Idle 0:01 10:14 10:15
Dies zeigt, wie jede qualifizierte Startreihe für den Benutzer ‚c‘ mit jeder qualifizierten Ende Reihe angepasst ist, viele unechten Reihen von Daten zu geben. Die NICHT VORHANDEN Unter Abfrage ein gemeinsames Thema ist, wenn sie mit zeitbasierte Abfragen zu tun. Sie können Informationen über diese Vorgänge in Snodgrass des „ Entwicklung zeitorientierten Anwendungen in SQL finden "(PDF online verfügbar unter URL), und in Datum, Darwen und Lorentzos" Temporal Daten und das relationale Modell “.
Andere Tipps
Ich bin ziemlich sicher, es kann nur mit SQL getan werden, es geht mir ziemlich viel Zeit mit einer Abfrage für Sie zu kommen zu nehmen, ich es in bearbeiten werde, wenn ich fertig bin. Die grundlegenden Schritte, die ich denke, wären zuerst die Höhe der Zeit zu berechnen jeweils eine (erfolgt durch jeden Eintrag zu nehmen und es zum nächsten Eintrag Füge- und Subtrahieren die Zeitdifferenz zu finden) nimmt dann eine einfache Gruppe von Klausel mit einer Summe wird leicht bekommen es in die Form haben Sie beschrieben.
Edit: Hier ist, was ich kam mit
SELECT l.userid, l.state, SUM(t.minutes) AS duration
FROM Log l
INNER JOIN (
SELECT l1.id, (l2.time - l1.time) AS minutes
FROM Log l1, Log l2
WHERE l2.time == (
-- find the next entry --
SELECT TOP 1 ls.time
FROM Log ls
WHERE ls.Time > l1.Time && ls.userid = l1.userid
ORDER BY ls.Time
)
) t ON l.id == t.id
GROUP BY l.userid, l.state
ORDER BY l.userid
Dieses halb-Pseudo-Code ist, dass ich aus alle Tabellennamen und Dinge, und Sie werden von einem anderen nicht nur einmal in der Lage zu subtrahieren, werden Sie wahrscheinlich die DATEDIFF-Funktion verwenden. Abgesehen davon aber, glaube ich, dass der Kern von ihm ist. Ich denke, SQL eine der erstaunlichsten Sprachen ist, können Sie fast alles, was mit wenig Code.