【问题标题】:Why stored procedure takes more time to execute compared to stored function for same code?与相同代码的存储函数相比,为什么存储过程需要更多时间来执行?
【发布时间】:2019-05-30 06:34:25
【问题描述】:

我创建了 stored-procedureOUT 参数来处理基于其他表值的自动化 INSERT 操作。我还为相同的操作制作了 stored-function。我面临一个问题,两者的执行时间有很大不同,这让我很担心,我不知道为什么会这样。

汇总下表值:

  • emp_personal 中的 2016 行

我正在使用MariaDB 10.3.14。我正在使用HeidiSQL 10.1.0.5577 进行测试。 我还尝试使用MariaDB Node.js ConnectorNode.JS 调用它。但在这两种情况下,我的结果几乎相同。

这是存储过程

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 */

Stored Function OutputStored Procedure Output

谁能解释一下为什么会这样?如果我做错了什么,请告诉我应该如何改正?

谢谢。

【问题讨论】:

    标签: mysql stored-procedures stored-functions


    【解决方案1】:

    当您将代码作为过程与函数运行时,类似代码的性能应该没有差异。

    最可能的解释(请参阅受影响的行)是在函数运行时,游标没有找到任何活动的人,而在程序运行中找到了,因此运行循环查询。

    【讨论】:

    • INSERT 操作已成功插入函数和过程中。它在两种情况下都能完美运行。我也有一些 isActive=0 值,所以总行数超过 2016 年。我附上了两个有问题的输出的屏幕截图。请检查。
    • 你在两次运行中使用完全相同的数据集吗?请注意,代码取决于假期的确定(顺便说一句,您有出勤变量初始化错误,因为您应该在循环内设置初始值)。
    • 是的,我在两次运行中都使用了相同的数据集。我已经初始化了出勤变量。 DECLARE attendance VARCHAR(20) DEFAULT 'Present';
    • 您在循环外有 attendance 初始化,而它应该在循环内(emp_personal 中的每一行单独完成)。
    • 我确实更新了循环内的值,但执行时间保持不变。我很担心为什么执行时间这么长。
    猜你喜欢
    • 2020-10-02
    • 1970-01-01
    • 2020-06-05
    • 2012-06-28
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多