SQL: Эффективно рассчитать максимальное количество одновременно зарегистрированных пользователей в интервале на основе исторической записи - PullRequest
0 голосов
/ 15 октября 2018

Я пытаюсь найти запрос, который находит истинное число максимальных одновременных пользователей в интервале на основе исторической записи пользовательских логинов (в данном случае интервал является ежедневным).

Приведенный ниже запрос будетвыполните эту работу, за исключением того, что для таблицы размером 500 тыс. записей выполнение запроса займет более 8 часов и в конечном итоге будет завершено до завершения.

SELECT  DATE(DBINFO('utc_to_datetime', login_utc)) AS utcDate,
        MAX(NVL(cumeins, 0) - NVL(cumeouts, 0)) AS Concurrent
FROM (
    SELECT  s.login_utc,
            (SELECT count(*)
             FROM root.haglog s2
                WHERE s2.login_utc <= s.login_utc
            ) AS cumeins,
            (SELECT count(*)
             FROM root.haglog s2
                WHERE s2.logout_utc <= s.login_utc
            ) AS cumeouts
    FROM root.haglog s GROUP BY login_utc
) s
GROUP BY 1 
ORDER BY 1 ASC;

Таким образом, поля login_utc / logout_utc не индексируются,но есть доступное индексированное поле с именем row_date (просто дата записи без компонента time).Я подумал, чтобы попытаться использовать этот индекс для ускорения процесса выбора, как показано ниже, так что мы сравниваем только с неиндексированными полями в том случае, если рассматриваемая строка уже совпадает с датой (намного меньшим подмножеством):

SELECT  DATE(DBINFO('utc_to_datetime', login_utc)) AS utcDate,
        MAX(NVL(cumeins, 0) - NVL(cumeouts, 0)) AS Concurrent
FROM (
    SELECT  s.login_utc,
            (SELECT count(*)
             FROM root.haglog s2
                WHERE CASE 
                        WHEN DATE(DBINFO('utc_to_datetime', s2.login_utc)) = s.row_date
                        THEN s2.login_utc <= s.login_utc
                        ELSE 0
                      END
            ) AS cumeins,
            (SELECT count(*)
             FROM root.haglog s2
                WHERE CASE 
                        WHEN DATE(DBINFO('utc_to_datetime', NVL(s2.logout_utc,0))) = s.row_date
                        THEN s2.logout_utc <= s.login_utc
                        ELSE 0
                      END
            ) AS cumeouts
    FROM root.haglog s GROUP BY login_utc, row_date
) s
GROUP BY 1 
ORDER BY 1 ASC;

Но оператор case, структурированный выше, приводит к синтаксической ошибке, вероятно потому, что он не может напрямую передать логику условного оператора.Я пытаюсь использовать здесь оператор case, потому что не гарантируется короткое традиционное вычисление схемы (в противном случае я бы просто сделал 'DATE (DBINFO (' utc_to_datetime ', s2.login_utc)) = s.row_date AND s2.login_utc <= s.login_utc'). </p>

Есть ли лучший способ уменьшить вычислительную сложность исходного запроса, и если нет, то как я могу структурировать запрос для использования индекса row_date?

UPDATE

Я опробовал предложение О. Джоунса и получил следующий запрос:

SELECT MAX(concurrency) concurrency, row_date FROM (
 SELECT s.row_date, t.t, COUNT(*) concurrency
   FROM root.haglog s
   JOIN (
        SELECT DISTINCT login_utc t FROM root.haglog s 
        UNION
        SELECT DISTINCT (NVL(logout_utc, 0) + 1) t FROM root.haglog s
        ) t ON s.login_utc >= t.t AND s.logout_utc < t.t   
  GROUP BY s.row_date, t.t 
) a GROUP BY row_date ORDER BY row_date ASC

Этот запрос был выполнен быстро, но он вернул только 7 результатов (набор данных - 500 КБ):

concurrency row_date
----------- ---------- 
          1 2018-05-04
          1 2018-06-05
          1 2018-06-26
          1 2018-07-10
          1 2018-08-10
          1 2018-09-10
          1 2018-09-19

Вот некоторые примеры данных (нет, на самом деле пользователь, вошедший в систему с мая), отсутствует:

row_date   seq_num    logid   login_utc  logout_utc logout_date 
---------- ---------- ------- ---------- ---------- ----------- 
2018-05-02 1525229404 1201111 1525247404 1525253897 2018-05-02  
2018-05-02 1525229463 1201112 1525247463 1525255395 2018-05-02  
2018-05-02 1525230080 1011111 1525248080 1525266019 2018-05-02  
2018-05-02 1525231420 1211111 1525249420 NULL       NULL  

Схема будет описана здесь: https://downloads.avaya.com/elmodocs2/contact_center/r3v11/780701_1/780701_1.pdf

ОБНОВЛЕНИЕ 2

Как для моего исходного запроса, так и для предложенного в ответе О. Джонсом, я вывел соответствующие столбцы во временную таблицу, сделал индексы для всех отношенийи до сих пор не идти.Запрос выполняется в течение пары часов, а затем уничтожается на сервере (клиент все еще считает, что он выполняется).На этом этапе, если бы я мог изменить свойство времени выполнения сервера, чтобы он мог просто работать до его завершения, это было бы хорошо ....

1 Ответ

0 голосов
/ 15 октября 2018

Вы ищете максимальное количество одновременных входов в систему, и у вас есть время login_utc и logout_utc.То есть, если вы рисуете эти временные интервалы на временной шкале, вы ищете максимальное количество перекрытий.

Извините, я не знаю, как Informix добавляет одну секунду к столбцу меток даты.Поэтому я использовал + INTERVAL 1 SEC.

Итак, начнем с получения всех различных временных точек на временной шкале

            SELECT DISTINCT s.login_utc t FROM root.haglog s
              WHERE row_date >= <<<start date of query>>>
                AND row_date < <<<day after end date of query >>>
            UNION
            SELECT DISTINCT s.logout_utc + INTERVAL 1 SEC t FROM root.haglog s
              WHERE row_date >= <<<start date of query>>>
                AND row_date < <<<day after end date of query >>>

Теперь используйте это для подсчета перекрытий в каждой временной точке.

     SELECT s.row_date. t.t, COUNT(*) concurrency
       FROM root.haglog s
       JOIN (
            SELECT DISTINCT s.login_utc t FROM root.haglog s
              WHERE row_date >= <<<start date of query>>>
                AND row_date < <<<day after end date of query >>>
            UNION
            SELECT DISTINCT s.logout_utc + INTERVAL 1 SEC t FROM root.haglog s
              WHERE row_date >= <<<start date of query>>>
                AND row_date < <<<day after end date of query >>>
            ) t ON s.login_utc >= t.t AND s.logout_utc < t.t    
      WHERE row_date >= <<<start date of query>>>
        AND row_date < <<<day after end date of query >>>
      GROUP BY s.row_date, t.t

Это дает вам ваш параллелизм в каждый момент времени t.Затем вы можете сделать это, чтобы получить параллелизм для каждого дня

       SELECT MAX(concurrency) concurrency, row_date
         FROM ( all that stuff ^^^^ ) a
        GROUP BY row_date

. Этот фильтр фильтрует row_date, чтобы ваш индекс помог уменьшить количество строк, которые нужно измельчить.

Если производительность ужасна, вы можете поместить набор результатов t.t во временную таблицу и проиндексировать ее.Или вы можете сделать то, что нужно для индексации хотя бы одного из login_utc и logout_utc.

...