Вопрос

Я ищу один запрос, который может преобразовать следующую информацию в таблице

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

к чему-то вроде этого (учитывая временной диапазон с 10 утра до 10:15 утра в качестве периода запроса)

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

Можно ли это сделать ТОЛЬКО с помощью SQL?Я использую Informix версии 11.5

Это было полезно?

Решение

Это может быть сделано с помощью одной инструкции SQL.Вот доказательство.

Настройка

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

Правильный запрос

Обратите внимание на условия.Таблица результатов должна исключать периоды между 'login' и первым другим событием;кроме того, он должен исключить период между 'Выходом из системы' и следующим событием (предположительно 'входом в систему').Самосоединение между таблицей на Имя столбец, а затем асимметричное соединение на время столбец (используя '<') гарантирует, что события происходят в определенном порядке по времени.Вложенный выбор "НЕ СУЩЕСТВУЕТ" гарантирует, что учитываются только смежные события.Использование МЕЖДУ И в подзапросе является ошибкой, поскольку оно включает его конечные точки, и крайне важно, чтобы r1.time и r2.time исключены из ассортимента;мне потребовалось несколько минут, чтобы обнаружить эту ошибку (запрос был выполнен, но не вернул ни одной строки, но почему?)!

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

Это приводит к ответу:

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

Значение "длительность" представляет собой ИНТЕРВАЛ ОТ ЧАСА До МИНУТЫ;если вы хотите получить значение всего за несколько минут, вам нужно преобразовать его с помощью приведения (используя 4 для точности, позволяющей использовать интервалы до 1440 минут или 1 дня;данные неоднозначны для более длительных временных интервалов):

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

Или:

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

IBM Informix Dynamic Server (IDS) имеет очень подробные обозначения для временных констант.В стандартном SQL вы могли бы использовать TIME в качестве типа и TIME '10:00:00' в качестве значения, но в строгом стандартном SQL секунды были бы необходимы.IDS предоставляет точные типы, которые нужны людям, например, DATETIME ОТ ЧАСА До МИНУТЫ.Вы бы также написали INTERVAL MINUTE(4) в стандартном SQL;параметр "ДО МИНУТЫ" должен быть необязательным.

Неверный запрос

В моем комментарии к ответу Рэя Хидаята я указал, что подзапрос EXISTS необходим для обеспечения того, чтобы рассматриваемые события были смежными - промежуточных событий нет.Вот тот же запрос с добавлением времени начала и окончания к выходным данным и отсутствием предложения EXISTS (а 'duration' переименован в 'lapse').:

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;

Это приводит к ответу:

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

Это показывает, как каждая подходящая начальная строка для пользователя 'c' сопоставляется с каждой подходящей конечной строкой, давая множество ложных строк данных.Вложенный запрос NOT EXISTS является распространенной темой при работе с запросами, основанными на времени.Вы можете найти информацию об этих операциях в "Разработка приложений, ориентированных на время, на SQL" (PDF доступен онлайн по URL-адресу), а в Date - Дарвен и Лоренцос ".Временные данные и Реляционная модель".

Другие советы

Я почти уверен, что это можно сделать, используя только SQL, мне потребуется довольно много времени, чтобы составить запрос для вас, я отредактирую его, когда закончу.Основными шагами, я думаю, сначала было бы вычислить количество времени, которое занимает каждый из них (выполняется путем взятия каждой записи и присоединения ее к следующей записи и вычитания, чтобы найти разницу во времени), затем простое предложение group by с суммой легко приведет его к форме, которую вы описали.

Редактировать:Вот что я придумал

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

Это полу-псевдокод, я придумал все названия таблиц и прочее, и вы не сможете просто вычесть одно время из другого, вы, вероятно, будете использовать функцию DATEDIFF .Кроме того, я думаю, что в этом-то и суть.Я думаю, что SQL - один из самых удивительных языков, вы можете делать практически все с небольшим количеством кода.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top