Можно ли использовать цикл «для каждого» в MySQL для создания временных таблиц с переменными?

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

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

Я знаю, что эта проблема намного лучше решается на C# (или любом другом языке, если на то пошло), но моя цель состоит в том, чтобы закодировать запрос, чтобы неквалифицированный администратор мог изменить столбец «дата», а затем 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  |
    +--------------+---------------+----------------+-------------------+--------------------+-------------+------------+

Это здорово, но чтобы получить нужные результаты, мне нужно закомментировать «Сотрудник1» и раскомментировать «Сотрудник2», каждый раз копируя и вставляя результаты в электронную таблицу. Я хотел бы печатать с циклом для каждого сотрудника, но я никогда не видел ничего подобного в 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)

person Bob Bass    schedule 01.07.2019    source источник
comment
Почему бы не использовать группу по сотруднику вместо цикла for? Если вы отредактируете свой вопрос и добавите схемы каждой таблицы (т. е. оператор CREATE TABLE), то мы сможем построить простой запрос для получения искомого результата. Наконец, вместо создания таблицы вы можете создать представление для получения результатов.   -  person Sloan Thrasher    schedule 01.07.2019
comment
Этот комментарий собирается взорвать меня как неофициального самоучку, но я не хочу изменять схему базы данных, даже если это просто означает добавление в нее 1 новой таблицы. ЕСЛИ Я не могу сохранить таблицу вне схемы callTrackingSoftware все вместе. Я просто никогда не знал, что это возможно.   -  person Bob Bass    schedule 01.07.2019
comment
Не предлагая никаких изменений в ваших таблицах. Просто прошу дополнительную информацию, чтобы я мог написать оператор SQL для получения результатов.   -  person Sloan Thrasher    schedule 01.07.2019
comment
Если у вас есть phpAdmin, он выведет оператор, используемый для создания таблицы. Просто сделайте это и добавьте сгенерированный код к вашему вопросу.   -  person Sloan Thrasher    schedule 01.07.2019
comment
Я использую рабочее место MySQL. Проблема в том, что данные, которые я извлекаю, взяты из двух отдельных таблиц, объединенных вместе. Мне очень жаль, я чувствую, что вы действительно пытаетесь помочь мне, и это идет прямо над моей головой. Я также скрыл некоторые имена таблиц, чтобы не допустить попадания конфиденциальной информации на эту страницу. Я довольно хорошо разбираюсь в MariaDB 5.7 (то, что я использую прямо сейчас), что вы пытаетесь сделать для меня? Вы заметили, что я использую объединенные таблицы для создания этой временной таблицы?   -  person Bob Bass    schedule 01.07.2019
comment
Чтобы получить оператор CREATE TABLE, который будет отображать схему таблицы, взгляните на это и оператор SQL show create table url_alias;. Добавление этой информации (даже если вы подделываете имена таблиц) предоставит информацию, необходимую для создания одного запроса, который даст результаты, которые вы запрашиваете. Просто запустите этот оператор для каждой из 2 таблиц, измените имена таблиц, если хотите в результате, затем вставьте их в свой вопрос, а затем отметьте как код.   -  person Sloan Thrasher    schedule 01.07.2019


Ответы (1)


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

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


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

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

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

Встроенное представление e здесь дает статический список из литералов, как указано в коде OP. Возможно, потребуется предварить литералы _latin1 или выполнить ПРЕОБРАЗОВАНИЕ с использованием набора символов, в котором сравнивается значение, чтобы избежать ошибки «набор символов не может быть приведен».

Еще лучше было бы использовать таблицу, на которую мы могли бы ссылаться, чтобы получить отдельный список имен сотрудников и использовать запрос этой таблицы во встроенном представлении, например. 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
person spencer7593    schedule 01.07.2019
comment
В хранимой процедуре или функции существует способ зацикливания и сохранения результата во временной таблице или возвращаемой переменной. Однако использование правильного запроса, как вы показали, является лучшим подходом. - person Sloan Thrasher; 01.07.2019
comment
@SloanThrasher: кажется, я упомянул об этом в своем ответе; конструкции процедурного цикла доступны внутри хранимой программы MySQL (объекты базы данных PROCEDURE, FUNCTION, TRIGGER), но не вне хранимых программ MySQL. Мое намерение состояло в том, чтобы упомянуть об этом. - person spencer7593; 02.07.2019