Почему хранимая процедура занимает больше времени по сравнению с хранимой функцией для того же кода? - PullRequest
0 голосов
/ 30 мая 2019

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

Ниже приведены итоговые значения таблицы:

  • 2016 строк в emp_personal

Я использую MariaDB 10.3.14. Я тестирую с HeidiSQL 10.1.0.5577. Я также попытался вызвать его из Node.JS, используя MariaDB Node.js Connector . Но в обоих случаях у меня был почти одинаковый результат.

Это хранимая процедура

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_auto_attendance`(
    OUT `result` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN 
DECLARE empId,done,isHoliday,isEmpHoliday,isWeekOff,isApprovedLeave,insertedRows INT DEFAULT 0;
DECLARE attendance VARCHAR(20) DEFAULT 'Present';
DECLARE empIds VARCHAR(500) DEFAULT '';

-- declare cursor with all employee Ids who are active
DECLARE empIds_cursor CURSOR FOR
SELECT id from emp_personal where isActive=1;

-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

-- Check if today is holiday
SELECT COUNT(id) INTO isHoliday
FROM apexa_portal.public_holiday_dates
WHERE dateOfHoliday= CURDATE(); 

OPEN empIds_cursor;
    get_id: LOOP
        SET attendance = 'Present';
        FETCH empIds_cursor INTO empId;

            -- Following check if NOT FOUND handler has called, if yes exit loop else continue. NOTE: If you don't write this, it will be infinite loop.
            IF done = 1 THEN 
                LEAVE get_id;
            END IF;

            -- Check if public holiday for employee
            IF isHoliday > 0 THEN
                SELECT COUNT(phtd.isActive) INTO isEmpHoliday FROM public_holiday_template_days as phtd 
                LEFT JOIN emp_office_details as eod ON eod.publicHolidayTemplate=phtd.templateId 
                LEFT JOIN public_holiday_dates as phd ON phd.pub_holiday_id=phtd.holidayId
                WHERE eod.empID=empId AND phd.dateOfHoliday=CURDATE() AND phtd.isActive=1;
                IF isEmpHoliday > 0 THEN
                    SET attendance = 'Public Holiday';
                END IF;
            END IF;

            -- if not public holiday, Check if week off
            IF attendance <> 'Public Holiday' THEN
                SELECT COUNT(eowd.id) INTO isWeekOff FROM emp_office_working_days as eowd
                WHERE DAY=DAYOFWEEK(CURDATE()) AND eowd.empId=empId AND eowd.isActive=0; 
                IF isWeekOff > 0 THEN
                    SET attendance = 'Week Off';
                END IF;
            END IF;

            -- if not week off, Check if approved leave
            IF attendance <> 'Week Off' THEN
                SELECT COUNT(la.id) INTO isApprovedLeave FROM leave_application as la
                WHERE la.fromDate <= CURDATE() AND la.toDate >= CURDATE() AND la.empID=empId AND la.leaveStatus='Approved';
                IF isApprovedLeave > 0 THEN
                    SET attendance = 'Approved Leave';
                END IF;
            END IF;

            -- insert attendance
            INSERT INTO attendance_master(empID,dateOfAttendance,attendance) VALUES(empId,CURDATE(),attendance);
            SET insertedRows = insertedRows + 1;
    END LOOP get_id;
CLOSE empIds_cursor;
SET result = insertedRows;
END

Это хранимая функция

CREATE DEFINER=`root`@`localhost` FUNCTION `test_auto_att_func`()
RETURNS int(11)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN 
DECLARE empId,done,isHoliday,isEmpHoliday,isWeekOff,isApprovedLeave,insertedRows INT DEFAULT 0;
DECLARE attendance VARCHAR(20) DEFAULT 'Present';
DECLARE empIds VARCHAR(500) DEFAULT '';

-- declare cursor with all employee Ids who are active
DECLARE empIds_cursor CURSOR FOR
SELECT id from emp_personal where isActive=1;

-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

-- Check if today is holiday
SELECT COUNT(id) INTO isHoliday
FROM apexa_portal.public_holiday_dates
WHERE dateOfHoliday= CURDATE(); 

OPEN empIds_cursor;
    get_id: LOOP
        SET attendance = 'Present';
        FETCH empIds_cursor INTO empId;

            -- Following check if NOT FOUND handler has called, if yes exit loop else continue. NOTE: If you don't write this, it will be infinite loop.
            IF done = 1 THEN 
                LEAVE get_id;
            END IF;

            -- Check if public holiday for employee
            IF isHoliday > 0 THEN
                SELECT COUNT(phtd.isActive) INTO isEmpHoliday FROM public_holiday_template_days as phtd 
                LEFT JOIN emp_office_details as eod ON eod.publicHolidayTemplate=phtd.templateId 
                LEFT JOIN public_holiday_dates as phd ON phd.pub_holiday_id=phtd.holidayId
                WHERE eod.empID=empId AND phd.dateOfHoliday=CURDATE() AND phtd.isActive=1;
                IF isEmpHoliday > 0 THEN
                    SET attendance = 'Public Holiday';
                END IF;
            END IF;

            -- if not public holiday, Check if week off
            IF attendance <> 'Public Holiday' THEN
                SELECT COUNT(eowd.id) INTO isWeekOff FROM emp_office_working_days as eowd
                WHERE DAY=DAYOFWEEK(CURDATE()) AND eowd.empId=empId AND eowd.isActive=0; 
                IF isWeekOff > 0 THEN
                    SET attendance = 'Week Off';
                END IF;
            END IF;

            -- if not week off, Check if approved leave
            IF attendance <> 'Week Off' THEN
                SELECT COUNT(la.id) INTO isApprovedLeave FROM leave_application as la
                WHERE la.fromDate <= CURDATE() AND la.toDate >= CURDATE() AND la.empID=empId AND la.leaveStatus='Approved';
                IF isApprovedLeave > 0 THEN
                    SET attendance = 'Approved Leave';
                END IF;
            END IF;

            -- insert attendance
            INSERT INTO attendance_master(empID,dateOfAttendance,attendance) VALUES(empId,CURDATE(),attendance);
            SET insertedRows = insertedRows + 1;
    END LOOP get_id;
CLOSE empIds_cursor;
RETURN insertedRows;

END

Время выполнения сохраненной функции

SELECT `test_auto_att_func`();
/* Affected rows: 0  Found rows: 1  Warnings: 0  Duration for 1 query: 0.328 sec. */

Время выполнения хранимой процедуры

CALL `sp_auto_attendance`(@res);
SELECT @res;
/* Affected rows: 6,049  Found rows: 1  Warnings: 0  Duration for 2 queries: 00:01:21.6 */

Выход сохраненной функции и Выход хранимой процедуры

Может кто-нибудь объяснить, почему это происходит? И если я делаю что-то не так, пожалуйста, дайте мне знать, как мне это исправить?

Спасибо.

1 Ответ

0 голосов
/ 30 мая 2019

Не должно быть различий в производительности аналогичного кода при запуске кода как процедуры по сравнению с функцией.

Наиболее вероятное объяснение (см. Затронутые строки) состоит в том, что во время выполнения функции курсор не нашел активных лиц, тогда как в ходе выполнения процедуры это было сделано, следовательно, выполнялись запросы цикла.

...