Можно ли использовать цикл «Для каждого» в MySQL для создания временных таблиц с переменными? - PullRequest
0 голосов
/ 01 июля 2019

Я пытаюсь создать временную таблицу, которая будет распечатывать статистику вызовов для моих сотрудников. Мне удалось создать довольно сложный набор запросов, подзапросов и переменных в операторе выбора, но я не могу придумать, как вывести более 1 строки данных во «временную таблицу».

Моя цель - в конечном итоге экспортировать отчеты о вызовах для моих сотрудников, поскольку они не интегрированы в наше программное обеспечение.

Я знаю, что эта проблема гораздо лучше решается в C # (или любом другом языке в этом отношении), но моя цель состоит в том, чтобы закодировать запрос, чтобы неквалифицированный администратор мог изменить столбец «date», а затем Workbench мог нарисовать вверх «отчет», который затем может быть экспортирован в виде .csv. Пока я могу печатать только 1 строку. Я пытаюсь выяснить, насколько далеко может зайти запрос MySQL или у меня больше места, чтобы можно было напечатать дополнительные строки.

-- Set Employee Name and Date for Variables
    SET @employee =
    'Employee1'
    -- 'Employee2'
    -- 'Employee3'
    -- 'Employee4'
    -- 'Employee5'
    , @inquirydate = '6/28/2019'
    ;

-- Count Total Calls (Valid/Invalid)
    SET @totalCalls = (
        SELECT COUNT(*)
        FROM
            callTrackingSoftware.log l
                JOIN
            callTrackingSoftware.dbase d ON l.filenumber = d.id
        WHERE
            l.logdate = @inquirydate
                AND l.collectorname IN (@employee)
                AND l.logmessage LIKE '%Called%');

-- Count Total (Valid Calls Only)
    SET @totalValidCalls = (
        SELECT COUNT(*)
        FROM
            callTrackingSoftware.log l
                JOIN
            callTrackingSoftware.dbase d ON l.filenumber = d.id
        WHERE
            l.logdate = @inquirydate
                AND l.collectorname IN (@employee)
                AND l.logmessage LIKE '%Called%'
                AND l.logmessage NOT LIKE '%Disconnected%'
                AND l.logmessage NOT LIKE '%VM Unavailable%'
                AND l.logmessage NOT LIKE '%No Answer%');

-- Count Total Unique Accounts (Count Once per FileNumber)
    SET @totalUniqueCalls = (
        SELECT DISTINCT COUNT(d.filenumber)
        FROM
            callTrackingSoftware.log l
                JOIN
            callTrackingSoftware.dbase d ON l.filenumber = d.id
        WHERE
            l.logdate = @inquirydate
                AND l.collectorname IN (@employee)
                AND l.logmessage LIKE '%Called%'
                AND l.logmessage NOT LIKE '%Disconnected%'
                AND l.logmessage NOT LIKE '%VM Unavailable%'
                AND l.logmessage NOT LIKE '%No Answer%');

-- Total Valid Calls
    SET @totalValidCalls = (
        SELECT COUNT(*)
        FROM
            callTrackingSoftware.log l
                JOIN
            callTrackingSoftware.dbase d ON l.filenumber = d.id
        WHERE
            l.logdate = @inquirydate
                AND l.collectorname IN (@employee)
                AND l.logmessage LIKE '%Called%'
                AND l.logmessage NOT LIKE '%Disconnected%'
                AND l.logmessage NOT LIKE '%VM Unavailable%'
                AND l.logmessage NOT LIKE '%No Answer%');

-- FIRST CALL TIME
    SET @firstCall = (
        SELECT (l.logtime)
        FROM
            callTrackingSoftware.log l
                JOIN
            callTrackingSoftware.dbase d ON l.filenumber = d.id
        WHERE
            l.logdate = @inquirydate
                AND l.collectorname IN (@employee)
                AND l.logmessage LIKE '%Called%'
                ORDER BY l.logdateandtime ASC
                LIMIT 1);

-- LAST CALL TIME
    SET @lastCall = (
        SELECT (l.logtime)
        FROM
            callTrackingSoftware.log l
                JOIN
            callTrackingSoftware.dbase d ON l.filenumber = d.id
        WHERE
            l.logdate = @inquirydate
                AND l.collectorname IN (@employee)
                AND l.logmessage LIKE '%Called%'
                ORDER BY l.logdateandtime DESC
                LIMIT 1);

-- PRINT VARIABLES
    select
        @employee as 'Employee',
        @inquirydate as 'Date',
        @totalCalls as 'TotalCalls',
        @totalValidCalls as 'TotalValidCalls',
        @totalUniqueCalls as 'TotalUniqueAccounts',
        @firstCall as 'FirstNotation',
        @lastCall as 'LastNotation';
    |  Employee    |       Date    |    TotalCalls  |  TotalValidCalls  |  TotalUniqueCalls  |  FirstCall  |  LastCall  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
> 1 |  Employee1   |    6/28/2019  |        133     |         64        |          64        |   11:17 AM  |   2:38 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
  2 |  Employee2   |    6/28/2019  |        135     |         50        |          50        |   11:00 AM  |   2:00 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
  3 |  Employee3   |    6/28/2019  |        200     |         60        |          60        |   11:50 AM  |   3:00 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
    etc... (one for each employee listed above)
    |  Employee    |       Date    |    TotalCalls  |  TotalValidCalls  |  TotalUniqueCalls  |  FirstCall  |  LastCall  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
> 1 |  Employee1   |    6/28/2019  |        133     |         64        |          64        |   11:17 AM  |   2:38 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+

Это здорово, но чтобы получить желаемые результаты, мне нужно закомментировать 'Employee1' и отменить комментарий 'Employee2', копируя и вставляя результаты в электронную таблицу каждый раз. Мне бы хотелось печатать с циклом для каждого сотрудника, но я никогда не видел ничего подобного в MySQL, кроме языка более низкого уровня. Я даже не уверен, что это возможно.

    -- IDEAL RESULTS:
    |  Employee    |       Date    |    TotalCalls  |  TotalValidCalls  |  TotalUniqueCalls  |  FirstCall  |  LastCall  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
> 1 |  Employee1   |    6/28/2019  |        133     |         64        |          64        |   11:17 AM  |   2:38 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
  2 |  Employee2   |    6/28/2019  |        135     |         50        |          50        |   11:00 AM  |   2:00 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
  3 |  Employee3   |    6/28/2019  |        200     |         60        |          60        |   11:50 AM  |   3:00 PM  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+
    -- etc... (one for each employee listed above)

1 Ответ

0 голосов
/ 01 июля 2019

Чтобы ответить на вопрос, который был задан:

Нет, в MySQL нет механизма для процедурного зацикливания нескольких значений пользовательских переменных вне хранимой программы MySQL (например, PROCEDURE of FUNCTION).


Более длинный ответ заключается в том, что нет необходимости в пользовательских переменных и циклах. Можно вернуть набор строк одним запросом, используя условное агрегирование.

(Мы отложим обсуждение нечетно отформатированного значения даты '6/28/2019' и просто предположим, что logdate хранится как тип данных MySQL DATE.)

Мы можем сгенерировать строки, которые нам нужны в наборе (и приспособить неизбежные пропущенные строки, пропуская «нулевое» число), используя встроенные представления c и e, чтобы вернуть набор дат, которые мы хотим вернуть, Крест присоединился к списку имен сотрудников.

Встроенное представление e здесь дает статический список из литералов, как указано в коде OP. Может потребоваться предварять литералы _latin1 или выполнять CONVERT с использованием набора символов, в котором сравнивается значение, чтобы избежать ошибки «набор символов не принудительный».

Еще лучше была бы таблица, на которую мы могли бы ссылаться, чтобы получить отдельный список имен сотрудников, и использовать запрос этой таблицы во встроенном представлении, например. SELECT ee.employee_name AS name_ FROM someothertable ee WHERE ... GROUP BY ee.employee_name

Примерно так:

    SELECT e.name_
         , c.date_

         , COUNT(
             IF( l.logmessage LIKE '%Called%'
             , d.id
             , NULL
             )
           ) AS cnt_total_calls

         , COUNT(
             IF( l.logmessage LIKE '%Called%'
             AND l.logmessage NOT LIKE '%Disconnected%'
             AND l.logmessage NOT LIKE '%VM Unavailable%'
             AND l.logmessage NOT LIKE '%No Answer%'
             , d.id
             , 0
             )
           ) AS cnt_valid_calls

         , COUNT( DISTINCT
             IF( l.logmessage LIKE '%Called%'
             AND l.logmessage NOT LIKE '%Disconnected%'
             AND l.logmessage NOT LIKE '%VM Unavailable%'
             AND l.logmessage NOT LIKE '%No Answer%'
             , l.filenumber
             , NULL
             )
           ) AS cnt_total_unique_account

         , MIN(
             IF( l.logmessage LIKE '%Called%'
             , l.logtime
             , NULL
             )
           ) AS first_call_time

         , MAX(
             IF( l.logmessage LIKE '%Called%'
             , l.logtime
             , NULL
             )
           ) AS last_call_time

      FROM ( 
             SELECT '2019-06-28' + INTERVAL 0 DAY AS date_
           ) c
     CROSS
      JOIN (
             SELECT 'Employee1' AS name_
             UNION ALL SELECT 'Employee2'
             UNION ALL SELECT 'Employee3'
             UNION ALL SELECT 'Employee4'
             UNION ALL SELECT 'Employee5'
           ) e
      LEFT
      JOIN callTrackingSoftware.log l
        ON l.collectorname  = e.name_
       AND l.logdate        = c.date_
      LEFT
      JOIN callTrackingSoftware.dbase d
        ON d.id  = l.filenumber

     GROUP
        BY e.name_
         , c.date_
     ORDER
        BY e.name_
         , c.date_

Обратите внимание на внешние объединения LEFT JOIN, чтобы можно было возвращать строки для комбинаций сотрудник / логдат, где ожидается нулевое число.

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

         , MAX(
             IF( l.logmessage LIKE '%Called%' AND d.id IS NOT NULL
             --                               ^^^^^^^^^^^^^^^^^^^^
             , l.logtime
             , NULL
             )
           ) AS last_call_time
...