如何在 MySQL 中使用 CURSOR 时在存储过程中的 FETCH NEXT 之后返回

How to move back after FETCH NEXT in stored procedure while using CURSOR in MySQL

以下是我的table:

CREATE TABLE IF NOT EXISTS `clockintest` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `PartnerId` bigint(20) unsigned DEFAULT NULL,
  `TimeStamp` datetime DEFAULT NULL,
  `City` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Street` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Postalcode` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Country` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `CommissionId` bigint(20) unsigned DEFAULT NULL,
  `WorkDayId` bigint(20) unsigned DEFAULT NULL,
  `UserId` bigint(20) unsigned DEFAULT NULL,
  `WorkDescription` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `EmployeeId` bigint(20) unsigned DEFAULT NULL,
  `Type` enum('Start','End') COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Kostenstelle` int(11) DEFAULT NULL,
  `Sachkonto` int(11) DEFAULT NULL,
  `Bearbeitungsschluessel` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `CreatedAt` datetime DEFAULT current_timestamp(),
  `UpdatedAt` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `DeletedAt` datetime DEFAULT NULL,
  `ExportedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Id` (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=712 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Dumping data for table basedb.clockintest: ~6 rows (approximately)
DELETE FROM `clockintest`;
/*!40000 ALTER TABLE `clockintest` DISABLE KEYS */;
INSERT INTO `clockintest` (`Id`, `PartnerId`, `TimeStamp`, `City`, `Street`, `Postalcode`, `Country`, `CommissionId`, `WorkDayId`, `UserId`, `WorkDescription`, `EmployeeId`, `Type`, `Kostenstelle`, `Sachkonto`, `Bearbeitungsschluessel`, `CreatedAt`, `UpdatedAt`, `DeletedAt`, `ExportedAt`) VALUES
    (645, 1, '2021-10-26 08:00:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'Start', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-27 11:43:20', NULL, NULL),
    (649, 1, '2021-10-26 08:00:00', NULL, NULL, NULL, NULL, NULL, 150, 1, NULL, 1, 'Start', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-29 13:02:50', NULL, NULL),
    (680, 1, '2021-10-26 10:00:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'End', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-28 08:46:05', NULL, NULL),
    (684, 1, '2021-10-26 12:00:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'Start', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-29 07:37:51', NULL, NULL),
    (709, 1, '2021-10-26 23:59:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'End', NULL, NULL, NULL, '2021-10-29 09:52:07', '2021-10-29 09:52:07', NULL, NULL),
    (710, 1, '2021-10-26 23:59:00', NULL, NULL, NULL, NULL, NULL, 150, 1, NULL, 1, 'End', NULL, NULL, NULL, '2021-10-29 09:52:07', '2021-10-29 13:02:44', NULL, NULL);

据此table我在MySQL中写下了一个Stored Procedure。我使用游标来检索行和进行计算。但是,我需要在两行之间进行计算,为此我需要下一步并向后移动。我使用 FETCH NEXT.

实现了下一个目的

但是,问题是在移动 FETCH NEXT 之后,我在一个循环执行中迭代了两行。这就是为什么当我有 4 行时(我应该迭代 4 次循环),但我只迭代了 2 次。

我该如何解决这个问题?有什么帮助吗?在我的 存储过程 .

之后
DELIMITER //
DROP PROCEDURE IF EXISTS calculation //

CREATE PROCEDURE calculation (IN workDayId INT)
BEGIN 
    DECLARE TimeSpan INT DEFAULT 0;
    DECLARE BreakTime INT DEFAULT 0;
    DECLARE Id INT DEFAULT 0;
        DECLARE c_Id INT DEFAULT 0;
            DECLARE c_TimeStamp DateTime;
                DECLARE c_WorkDayId INT DEFAULT 0;
                    DECLARE c_EmployeeId INT DEFAULT 0;
                        DECLARE c_Type VARCHAR(10);
                            DECLARE n_TimeStamp DateTime;
                                DECLARE n_Id INT DEFAULT 0;
                                    DECLARE n_WorkDayId INT DEFAULT 0;
                                        DECLARE n_EmployeeId INT DEFAULT 0;
                                            DECLARE n_Type VARCHAR(10);
                                                    DECLARE cur_Type VARCHAR(100) DEFAULT "";
    DECLARE done INT DEFAULT FALSE;
    DECLARE cursor_clockIn CURSOR FOR SELECT c.Id, c.TimeStamp, c.WorkDayId, c.EmployeeId, c.`Type` FROM clockintest c WHERE c.WorkDayId = Id ORDER BY c.TimeStamp;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    SET Id = workDayId;
    OPEN cursor_clockIn;
    SET TimeSpan = 0;
    SET BreakTime = 0;
    loop_through_rows: LOOP
        FETCH cursor_clockIn INTO c_Id, c_TimeStamp, c_WorkDayId, c_EmployeeId, c_Type;
        BEGIN
        FETCH NEXT FROM cursor_clockIn INTO n_Id, n_TimeStamp, n_WorkDayId, n_EmployeeId, n_Type;
    
        IF done THEN 
            LEAVE loop_through_rows;
        END IF;
        
        IF c_Type = 'Start' THEN
            SET TimeSpan = TimeSpan + TIMESTAMPDIFF(MICROSECOND, c_TimeStamp, n_TimeStamp) / 1000;
        END IF;
        
        IF c_Type = 'End' THEN
            SET BreakTime = BreakTime + TIMESTAMPDIFF(MICROSECOND, c_TimeStamp, n_TimeStamp) / 1000;
        END IF;

        
            SET cur_Type = CONCAT(cur_Type,';', c_Id );
            END;
    END LOOP;
    SELECT TimeSpan, BreakTime, cur_Type;
        UPDATE workday w SET w.TimeSpan = TimeSpan, w.BreakTime = BreakTime WHERE w.Id = workDayId;

    CLOSE cursor_clockIn;

END //

DELIMITER ;

CALL calculation (149);

好吧,这很棘手

我改变了你的程序。

游标现在直接使用变量,您的构造不会给出任何错误,但也不会产生正确的结果

第一次提取现在在循环之外,所以整个事情都被初始化了。

添加总结的结尾,我添加了 pat,其中我将所有 c_... 变量设置为 n_... 的值,以便例程可以进入下一个循环

此外,如您所见,良好的缩进有助于更好地理解概念,因为您可以看到事物的开始和结束位置。这也是对未来的一个好建议

CREATE TABLE IF NOT EXISTS `clockintest` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `PartnerId` bigint(20) unsigned DEFAULT NULL,
  `TimeStamp` datetime DEFAULT NULL,
  `City` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Street` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Postalcode` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Country` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `CommissionId` bigint(20) unsigned DEFAULT NULL,
  `WorkDayId` bigint(20) unsigned DEFAULT NULL,
  `UserId` bigint(20) unsigned DEFAULT NULL,
  `WorkDescription` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `EmployeeId` bigint(20) unsigned DEFAULT NULL,
  `Type` enum('Start','End') COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Kostenstelle` int(11) DEFAULT NULL,
  `Sachkonto` int(11) DEFAULT NULL,
  `Bearbeitungsschluessel` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `CreatedAt` datetime DEFAULT current_timestamp(),
  `UpdatedAt` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `DeletedAt` datetime DEFAULT NULL,
  `ExportedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Id` (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=712 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Dumping data for table basedb.clockintest: ~6 rows (approximately)
DELETE FROM `clockintest`;
/*!40000 ALTER TABLE `clockintest` DISABLE KEYS */;
INSERT INTO `clockintest` (`Id`, `PartnerId`, `TimeStamp`, `City`, `Street`, `Postalcode`, `Country`, `CommissionId`, `WorkDayId`, `UserId`, `WorkDescription`, `EmployeeId`, `Type`, `Kostenstelle`, `Sachkonto`, `Bearbeitungsschluessel`, `CreatedAt`, `UpdatedAt`, `DeletedAt`, `ExportedAt`) VALUES
    (645, 1, '2021-10-26 08:00:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'Start', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-27 11:43:20', NULL, NULL),
    (649, 1, '2021-10-26 08:00:00', NULL, NULL, NULL, NULL, NULL, 150, 1, NULL, 1, 'Start', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-29 13:02:50', NULL, NULL),
    (680, 1, '2021-10-26 10:00:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'End', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-28 08:46:05', NULL, NULL),
    (684, 1, '2021-10-26 12:00:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'Start', NULL, NULL, NULL, '2021-10-27 11:41:29', '2021-10-29 07:37:51', NULL, NULL),
    (709, 1, '2021-10-26 23:59:00', NULL, NULL, NULL, NULL, NULL, 149, 1, NULL, 1, 'End', NULL, NULL, NULL, '2021-10-29 09:52:07', '2021-10-29 09:52:07', NULL, NULL),
    (710, 1, '2021-10-26 23:59:00', NULL, NULL, NULL, NULL, NULL, 150, 1, NULL, 1, 'End', NULL, NULL, NULL, '2021-10-29 09:52:07', '2021-10-29 13:02:44', NULL, NULL);
CREATE TABLE workday (id int, TimeSpan bigint, BreakTime BIGINT )
INSERT INTO workday VALUES (149,0,0)
SELECT c.Id, c.TimeStamp, c.WorkDayId, c.EmployeeId, c.`Type` 
                       FROM clockintest c WHERE c.WorkDayId = 149 ORDER BY c.TimeStamp;
 Id | TimeStamp           | WorkDayId | EmployeeId | Type 
--: | :------------------ | --------: | ---------: | :----
645 | 2021-10-26 08:00:00 |       149 |          1 | Start
680 | 2021-10-26 10:00:00 |       149 |          1 | End  
684 | 2021-10-26 12:00:00 |       149 |          1 | Start
709 | 2021-10-26 23:59:00 |       149 |          1 | End  
DROP PROCEDURE IF EXISTS calculation ;

CREATE PROCEDURE calculation (IN workDayId INT)
BEGIN 
    DECLARE TimeSpan INT DEFAULT 0;
    DECLARE BreakTime INT DEFAULT 0;
    DECLARE c_Id INT DEFAULT 0;
    DECLARE c_TimeStamp DateTime;
    DECLARE c_WorkDayId INT DEFAULT 0;
    DECLARE c_EmployeeId INT DEFAULT 0;
    DECLARE c_Type VARCHAR(10);
    DECLARE n_TimeStamp DateTime;
    DECLARE n_Id INT DEFAULT 0;
    DECLARE n_WorkDayId INT DEFAULT 0;
    DECLARE n_EmployeeId INT DEFAULT 0;
    DECLARE n_Type VARCHAR(10);
    DECLARE cur_Type VARCHAR(100) DEFAULT "";
    DECLARE done INT DEFAULT FALSE;
    DECLARE cursor_clockIn CURSOR FOR SELECT c.Id, c.TimeStamp, c.WorkDayId, c.EmployeeId, c.`Type` 
                       FROM clockintest c WHERE c.WorkDayId = workDayId ORDER BY c.TimeStamp;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    OPEN cursor_clockIn;
    SET TimeSpan = 0;
    SET BreakTime = 0;
        FETCH cursor_clockIn INTO c_Id, c_TimeStamp, c_WorkDayId, c_EmployeeId, c_Type;
        SET @a = c_Id;  
    loop_through_rows: LOOP

        BEGIN
          
        FETCH NEXT FROM cursor_clockIn INTO n_Id, n_TimeStamp, n_WorkDayId, n_EmployeeId, n_Type;

        IF done THEN 
            LEAVE loop_through_rows;
        END IF;
      SET @a = CONCAT(@a,';', n_id);      
        IF c_Type = 'Start' THEN
            SET TimeSpan = TimeSpan + TIMESTAMPDIFF(MICROSECOND, c_TimeStamp, n_TimeStamp) / 1000;
        END IF;
        
        IF c_Type = 'End' THEN
            SET BreakTime = BreakTime + TIMESTAMPDIFF(MICROSECOND, c_TimeStamp, n_TimeStamp) / 1000;
        END IF;

        
            SET cur_Type = CONCAT(cur_Type,';', c_Id );
            SET c_Id = n_id;
            SET c_TimeStamp = n_TimeStamp;
            SET c_WorkDayId = n_WorkDayId;
            SET c_EmployeeId = n_EmployeeId;
            SET c_Type = n_Type;
            END;
    END LOOP;
    SELECT TimeSpan, BreakTime, cur_Type;
        UPDATE workday w SET w.TimeSpan = TimeSpan, w.BreakTime = BreakTime WHERE w.Id = workDayId;

    CLOSE cursor_clockIn;

END 
CALL calculation (149);
TimeSpan | BreakTime | cur_Type    
-------: | --------: | :-----------
50340000 |   7200000 | ;645;680;684

✓
SELECT @a
| @a              |
| :-------------- |
| 645;680;684;709 |
SELECT * FROM workday
 id | TimeSpan | BreakTime
--: | -------: | --------:
149 | 50340000 |   7200000

db<>fiddle here