MySQL 通过过程进行的变量分配无法正常工作
MySQL Variable Assignment via Procedure Not Working Correctly
在下面的代码中,我尝试逐行查看 endDateTable
的结果,将当前行的 endDate
与前一行的 endDate
进行比较。如果自上次以来有任何变化,我们增加 @revisionNum
。但是,在填充新 table 后,所有 @revisionNum
条目都是 0
。我做错了什么?
注意:我以这种方式使用准备好的语句,因为对变量执行直接 SELECT
会给出语法错误,因为 LIMIT
子句不允许在我们的版本中使用变量 MySQL.
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 $$
您不需要循环,您可以使用 INSERT INTO ... SELECT ...
,在 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
LIMIT
子句中的偏移值在没有 ORDER BY
的情况下很脆弱。
没有 ORDER BY
子句,MySQL 可以自由 return 结果以任何顺序排列。
无法保证 LIMIT 41,1
会 return LIMIT 42,1
之前的行,或者它不会 return 与 LIMIT 13,1
完全相同的行] 做到了。
(关系数据库中的 table 表示一组无序的元组,没有保证 "order" 或 table 中的行。)
但仅将 ORDER BY
添加到查询中不足以解决 Rube-Goldberg 式的冗长代码。
在显示的代码中,看起来每次通过循环,我们都将 endDateTable
的副本插入到 finalTable
中。如果在 endDateTable 中有 1,000 行,我们将在 finalTable 中插入 1,000,000 行 (1,000 x 1,000)。完全不清楚为什么我们需要这么多副本。
根据显示的代码,尚不清楚 objective 是什么。看起来我们正在有条件地递增 revisionNum,其最终结果是最高修订号。这里只是猜测。
如果在 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 执行更有效。
在下面的代码中,我尝试逐行查看 endDateTable
的结果,将当前行的 endDate
与前一行的 endDate
进行比较。如果自上次以来有任何变化,我们增加 @revisionNum
。但是,在填充新 table 后,所有 @revisionNum
条目都是 0
。我做错了什么?
注意:我以这种方式使用准备好的语句,因为对变量执行直接 SELECT
会给出语法错误,因为 LIMIT
子句不允许在我们的版本中使用变量 MySQL.
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 $$
您不需要循环,您可以使用 INSERT INTO ... SELECT ...
,在 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
LIMIT
子句中的偏移值在没有 ORDER BY
的情况下很脆弱。
没有 ORDER BY
子句,MySQL 可以自由 return 结果以任何顺序排列。
无法保证 LIMIT 41,1
会 return LIMIT 42,1
之前的行,或者它不会 return 与 LIMIT 13,1
完全相同的行] 做到了。
(关系数据库中的 table 表示一组无序的元组,没有保证 "order" 或 table 中的行。)
但仅将 ORDER BY
添加到查询中不足以解决 Rube-Goldberg 式的冗长代码。
在显示的代码中,看起来每次通过循环,我们都将 endDateTable
的副本插入到 finalTable
中。如果在 endDateTable 中有 1,000 行,我们将在 finalTable 中插入 1,000,000 行 (1,000 x 1,000)。完全不清楚为什么我们需要这么多副本。
根据显示的代码,尚不清楚 objective 是什么。看起来我们正在有条件地递增 revisionNum,其最终结果是最高修订号。这里只是猜测。
如果在 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 执行更有效。