通过过程的MySQL变量分配无法正常工作

时间:2019-08-13 16:31:43

标签: mysql loops variables stored-procedures prepared-statement

在下面的代码中,我试图逐行浏览endDateTable的结果,将当前行的endDate与上一行的endDate进行比较。如果自上次以来没有任何变化,我们将增加@revisionNum。但是,在填充新表后,所有@revisionNum条目都是0。我在做什么错了?

注意:我正在以这种方式使用准备好的语句,因为对SELECT子句不允许在我们的MySQL版本中使用变量,直接对变量执行LIMIT会产生语法错误。 / p>

BEGIN

 DECLARE _currentEndDate DATETIME DEFAULT now();
 DECLARE _priorEndDate DATETIME DEFAULT now();

 SET @ResultsCount = (SELECT COUNT(*) FROM mainTable);

 SET @j = 0;
    WHILE @j < @ResultsCount DO

        SET @revisionNum = 0;

        /*CURRENT END DATE*/
        SET @appResultQueryCurrent = CONCAT('
            SELECT 
                end_date
            INTO _currentEndDate
            FROM endDateTable 
            LIMIT ', @j, ', 1'
        );      

        PREPARE currentQueryStmt FROM @appResultQueryCurrent;
        EXECUTE currentQueryStmt;

        /*PREVIOUS END DATE*/
        SET @appResultQueryPrior = CONCAT('
            SELECT
                end_date
            INTO _priorAppEndDate
            FROM endDateTable
            LIMIT ', IF(@j = 0, 0, @j - 1), ', 1'
        );

        PREPARE priorQueryStmt FROM @appResultQueryPrior;
        EXECUTE priorQueryStmt; 

        SET @revisionNum = IF(
                @j = 0 OR (_currentEndDate = _priorEndDate),
                @revisionNum,
                IF(
                    _currentEndDate != _priorEndDate,
                    @revisionNum + 1,
                    @revisionNum
                )
            );

        INSERT INTO finalTable (RevisionNum)
        SELECT 
            @revisionNum AS RevisionNum 
        FROM endDateTable;

    SET @j = @j +1;

 END WHILE;

END $$

2 个答案:

答案 0 :(得分:1)

您不需要循环,可以使用INSERT INTO ... SELECT ...,在选择查询中增加变量。

您还需要一个ORDER BY条件来指定在将一行与上一行进行比较时如何对行进行排序。

INSERT INTO finalTable (RevisionNum, otherColumn)
SELECT revision, otherColumn
FROM (
    SELECT IF(end_date = @prev_end_date, @revision, @revision := @revision + 1) AS revision,
            @prev_end_date := end_date,
            otherColumn
    FROM endDateTable
    CROSS JOIN (SELECT @prev_end_date := NULL, @revision := -1) AS vars
    ORDER BY id) AS x

DEMO

答案 1 :(得分:0)

LIMIT子句中的偏移量值很小,没有ORDER BY

没有ORDER BY子句,MySQL可以按任意顺序自由返回结果。

不能保证LIMIT 41,1将返回LIMIT 42,1之前的行,也不保证它不会返回与LIMIT 13,1完全相同的行。

(关系数据库中的表表示无序的元组集,表中没有保证的“顺序”或行。)

但是仅向查询中添加ORDER BY不足以解决Rube-Goldberg式的僵局。

在所示的代码中,每次循环似乎都在endDateTable中插入finalTable的副本。如果endDateTable中有1,000行,我们将在finalTable中插入1,000,000行(1,000 x 1,000)。根本不清楚为什么我们需要那么多副本。

鉴于显示的代码,不清楚目标是什么。看起来我们有条件地增加了versionNum,其最终结果是最高的修订num。只是在这里猜测。

如果在某种过程的LOOP构造中需要执行此操作,那么我认为我们将进行游标循环。而且我们可以使用过程变量还是用户定义变量。

遵循以下原则:

BEGIN
  DECLARE ld_current_end_date  DATETIME;
  DECLARE ld_prior_end_date    DATETIME;
  DECLARE li_done              INT;
  DECLARE li_revision_num      INT;
  DECLARE lcsr_end_date CURSOR FOR SELECT t.end_date FROM `endDateTable` t ORDER BY NULL;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET li_done = TRUE;
  SET li_done = FALSE;
  SET li_revision_num = 0;
  OPEN lcsr_end_date;
  FETCH lcsr_end_date INTO ld_current_end_date;
  SET ld_prior_end_date = ld_current_end_date;
  WHILE NOT li_done DO
    SET li_revision_num = li_revision_num + IF( ld_current_end_date <=> ld_prior_end_date ,0,1);
    SET ld_prior_end_date := ld_current_end_date;
    FETCH lcsr_end_date INTO ld_current_end_date;
  END WHILE;
  CLOSE lcsr_end_date;
  INSERT INTO `finalTable` (revisionnum) VALUES (li_revision_num);
END $$

请注意SELECT上的“ order by”子句,它不清楚应该对行进行排序,因此我们将文字用作占位符。

作为最终结果,我们在finalTable中插入一行。

同样,尚不清楚问题中的代码应该实现什么,但是在有序行上进行游标循环要比获取单个行的数十亿个动态SQL执行效率更高。