Question

Je cherche une seule requête capable de convertir les informations suivantes dans la table

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

vers quelque chose comme ceci (étant donné l'intervalle de temps compris entre 10 h et 10 h 15)

name: State    :Duration
a   : chatting :2 Minutes 
a   : Idle     :2 Minutes
b   : chatting :1 Minute

Cela peut-il être fait UNIQUEMENT en utilisant SQL? J'utilise Informix version 11.5

Était-ce utile?

La solution

Cela peut être fait en une seule instruction SQL. Voici la preuve.

Configuration

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');

Requête correcte

Notez les conditions. La table de résultats doit exclure les points entre "login" et le premier autre événement. de plus, il doit exclure la période entre "Déconnexion" et le prochain événement (vraisemblablement un "login"). La jointure automatique entre la table de la colonne nom , puis la jointure asymétrique de la colonne heure (à l'aide de '<') garantit que les événements se déroulent dans l'ordre chronologique. La sous-sélection NOT EXISTS garantit que seuls les événements adjacents sont pris en compte. L'utilisation de BETWEEN AND dans la sous-requête est une erreur car elle inclut ses extrémités et il est essentiel que r1.time et r2.time soient exclus de la plage; il m'a fallu quelques minutes pour repérer ce bogue (la requête a fonctionné mais n'a renvoyé aucune ligne, mais pourquoi ?)!

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);

Ceci produit la réponse:

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

La valeur de 'durée' est un intervalle entre heures et minutes; si vous voulez une valeur en quelques minutes seulement, vous devez la convertir avec une conversion (utilisez 4 pour la précision afin de permettre des intervalles allant jusqu'à 1440 minutes ou 1 jour; les données sont ambiguës pour des périodes plus longues):

(r2.time - r1.time)::INTERVAL MINUTE(4) TO MINUTE

Ou:

CAST (r2.time - r1.time AS INTERVAL MINUTE(4) TO MINUTE)

IBM Informix Dynamic Server (IDS) comporte des notations très détaillées pour les constantes de temps. En SQL standard, vous pouvez utiliser TIME en tant que type et TIME '10: 00: 00 'en tant que valeur, mais les secondes sont nécessaires en SQL strict. IDS fournit les types exacts souhaités par les utilisateurs - tels que DATETIME HEURE À MINUTE. Vous pouvez également écrire INTERVAL MINUTE (4) en SQL standard; "À MINUTE" devrait être facultatif.

Requête incorrecte

Dans ma réponse à la réponse de Ray Hidayat, j’ai souligné que la sous-requête EXISTS est nécessaire pour garantir que les événements considérés sont contigus - il n’existe aucun événement intermédiaire. Voici la même requête avec les heures de début et de fin ajoutées à la sortie et la clause EXISTS manquante (et "durée" renommé en "caduc"):

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;

Ceci produit la réponse:

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

Ceci montre comment chaque ligne de début éligible pour l'utilisateur 'c' est appariée avec chaque ligne de fin éligible, donnant ainsi de nombreuses lignes de données parasites. La sous-requête NOT EXISTS est un thème commun lors du traitement de requêtes temporelles. Vous pouvez trouver des informations sur ces opérations dans le & Quot; développement d'applications orientées temps de Snodgrass en SQL " (PDF disponible en ligne à l'adresse URL) et dans Date, Darwen et Lorentzos & Quot; Données temporelles et modèle relationnel & ";

Autres conseils

Je suis presque sûr que cela peut être fait en utilisant uniquement du SQL, cela va me prendre un peu de temps pour trouver une requête pour vous, je la modifierai quand j'aurai terminé. Je pense que les étapes de base seraient d’abord de calculer le temps que chacune prend (faites en prenant chaque entrée et en la joignant à l’entrée suivante et en soustrayant pour trouver la différence de temps), puis un simple groupe par clause avec une somme sera facilement obtenu dans la forme que vous avez décrite.

Edit: voici ce que j'ai trouvé

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

Il s’agit d’un semi-pseudo-code, j’ai composé tous les noms de tables et d’autres choses, et vous ne pourrez pas simplement soustraire une fois, vous utiliserez probablement la fonction DATEDIFF. En plus de cela, je pense que c'est l'essentiel. Je pense que SQL est l’un des langages les plus étonnants, vous pouvez faire presque tout avec peu de code.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top