在不使用存储过程的情况下循环 n 次
Posted
技术标签:
【中文标题】在不使用存储过程的情况下循环 n 次【英文标题】:Loop n times without using a stored procedure 【发布时间】:2015-09-03 05:57:33 【问题描述】:如何在不使用存储过程的情况下编写一个在 mysql 中运行 n 次的循环。
这就是我使用存储过程的方式:
DELIMITER $$
DROP PROCEDURE IF EXISTS test$$
CREATE PROCEDURE test()
BEGIN
DECLARE count INT DEFAULT 0;
WHILE count < 10 DO
/**Sql statement**/
SET count = count + 1;
END WHILE;
END$$
DELIMITER ;
然后我以这种方式执行我的程序:
call test();
如果我删除存储过程并运行正常查询,那么它会失败并出现以下错误:
1064 - 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以获取在 'DECLARE count INT DEFAULT 0; 附近使用的正确语法; WHILE count
我通过互联网寻找解决方案,但没有运气。
基于 cmets 编辑:
上面的存储过程完全符合我的要求:循环 10 次并执行我的 sql 语句。现在我想在不使用存储过程的情况下完成同样的事情。类似的东西:
DECLARE count INT DEFAULT 0;
WHILE count < 10 DO
/**Sql statement**/
SET count = count + 1;
END WHILE;
【问题讨论】:
我对mysql一无所知,但不能是因为count in a reserved关键字吗? @HoneyBadger 我尝试使用不同的名称,它不起作用 不清楚您的要求。你有你的存储过程,你调用你的过程。之后您删除了该过程并像SELECT 10;
一样运行正常查询?如果是这样,您将永远不会收到您发布的错误消息。因此,请向我们展示您尝试运行的查询。
我明白,它并没有真正回答这个问题,但你的主要查询是做什么的?也许可以将其重写为一个没有显式循环的查询。
@JuanCarlosOropeza 我的 sql 语句是一个基本的 INSERT 查询,比如INSERT INTO table_name(col1,col2,col3) VALUES("val1","val2",count)
【参考方案1】:
Flow Control Statements 上的 MySQL 文档说:
MySQL 支持 IF、CASE、ITERATE、LEAVE LOOP、WHILE 和 REPEAT 在存储程序中的流控制构造。
Stored Programs and Views 上的文档说:
存储的程序定义包括一个可以使用复合的主体 语句、循环、条件和声明的变量。
Compound-Statement Syntax
本节介绍 BEGIN ... END 组合的语法 语句和其他可以在 stored 的正文中使用的语句 程序:存储过程和函数、触发器和事件。
复合语句是可以包含其他块的块; 变量、条件处理程序和游标的声明;和流动 控制结构,例如循环和条件测试。
因此,看起来您只能在存储过程、函数或触发器中运行显式循环。
根据您在 SQL 语句中执行的操作,使用数字表(或视图)(Creating a "Numbers Table" in mysql、MYSQL: Sequential Number Table)可能是可以接受的。
如果您的查询是 SELECT
并且可以将 SELECT
的结果作为一个长结果集返回 10 次(而不是 10 个单独的结果集),您可以执行以下操作:
SELECT MainQuery.*
FROM
(
SELECT 1 AS Number
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9
UNION ALL SELECT 10
) AS Numbers
CROSS JOIN
(
SELECT 'some data' AS Result
) AS MainQuery
插入示例
我建议在您的数据库中拥有一个永久的数字表。它在很多情况下都很有用。请参阅上面的链接如何生成它。
因此,如果您有一个表 Numbers
和 int
列 Number
其值从 1 到 100K(如我所做的那样),并且此列上的主键,那么而不是这个循环:
DECLARE count INT DEFAULT 0;
WHILE count < 10 DO
INSERT INTO table_name(col1,col2,col3)
VALUES("val1","val2",count);
SET count = count + 1;
END WHILE;
你可以写:
INSERT INTO table_name(col1,col2,col3)
SELECT ("val1", "val2", Numbers.Number-1)
FROM Numbers
WHERE Numbers.Number <= 10;
它的工作速度也会快近 10 倍。
【讨论】:
@LuthandoLoot,谢谢。您也可以使用带有INSERT
的数字表。您可以编写一个同时插入 10 行的语句,而不是循环中的 10 个单独语句。
我如何只用一个查询来做到这一点?
@LuthandoLoot,我为INSERT
添加了示例。【参考方案2】:
您可以使用 MariaDB 序列引擎直接执行此操作。 MariaDB 是 MySQL 的二进制替代品。
“序列引擎允许创建具有给定起始值、结束值和增量的数字(正整数)的升序或降序序列。”
[手动序列引擎]
以下是一些示例:
mysql -uroot -p
Enter password: xxxxxxx
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.0.20-MariaDB-log Homebrew
Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> use tmp
Database changed
MariaDB [tmp]> select version();
+---------------------+
| version() |
+---------------------+
| 10.0.20-MariaDB-log |
+---------------------+
1 row in set (0.00 sec)
MariaDB [tmp]> select * from seq_1_to_10;
+-----+
| seq |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+-----+
10 rows in set (0.00 sec)
MariaDB [tmp]> select * from seq_1_to_10_step_2;
+-----+
| seq |
+-----+
| 1 |
| 3 |
| 5 |
| 7 |
| 9 |
+-----+
5 rows in set (0.00 sec)
MariaDB [tmp]> SELECT DAYNAME('1980-12-05' + INTERVAL (seq) YEAR) day,
-> '1980-12-05' + INTERVAL (seq) YEAR date FROM seq_0_to_40;
+-----------+------------+
| day | date |
+-----------+------------+
| Friday | 1980-12-05 |
| Saturday | 1981-12-05 |
| Sunday | 1982-12-05 |
| Monday | 1983-12-05 |
| Wednesday | 1984-12-05 |
| Thursday | 1985-12-05 |
| Friday | 1986-12-05 |
| Saturday | 1987-12-05 |
| Monday | 1988-12-05 |
| Tuesday | 1989-12-05 |
| Wednesday | 1990-12-05 |
| Thursday | 1991-12-05 |
| Saturday | 1992-12-05 |
| Sunday | 1993-12-05 |
| Monday | 1994-12-05 |
| Tuesday | 1995-12-05 |
| Thursday | 1996-12-05 |
| Friday | 1997-12-05 |
| Saturday | 1998-12-05 |
| Sunday | 1999-12-05 |
| Tuesday | 2000-12-05 |
| Wednesday | 2001-12-05 |
| Thursday | 2002-12-05 |
| Friday | 2003-12-05 |
| Sunday | 2004-12-05 |
| Monday | 2005-12-05 |
| Tuesday | 2006-12-05 |
| Wednesday | 2007-12-05 |
| Friday | 2008-12-05 |
| Saturday | 2009-12-05 |
| Sunday | 2010-12-05 |
| Monday | 2011-12-05 |
| Wednesday | 2012-12-05 |
| Thursday | 2013-12-05 |
| Friday | 2014-12-05 |
| Saturday | 2015-12-05 |
| Monday | 2016-12-05 |
| Tuesday | 2017-12-05 |
| Wednesday | 2018-12-05 |
| Thursday | 2019-12-05 |
| Saturday | 2020-12-05 |
+-----------+------------+
41 rows in set (0.00 sec)
MariaDB [tmp]>
这里有一个示例:
MariaDB [(none)]> use tmp
Database changed
MariaDB [tmp]> SELECT * FROM seq_1_to_5,
-> (SELECT * FROM animals) AS x
-> ORDER BY seq;
+-----+------+-----------+-----------------+
| seq | id | name | specie |
+-----+------+-----------+-----------------+
| 1 | 1 | dougie | dog-poodle |
| 1 | 6 | tweety | bird-canary |
| 1 | 5 | spotty | turtle-spotted |
| 1 | 4 | mr.turtle | turtle-snapping |
| 1 | 3 | cadi | cat-persian |
| 1 | 2 | bonzo | dog-pitbull |
| 2 | 4 | mr.turtle | turtle-snapping |
| 2 | 3 | cadi | cat-persian |
| 2 | 2 | bonzo | dog-pitbull |
| 2 | 1 | dougie | dog-poodle |
| 2 | 6 | tweety | bird-canary |
| 2 | 5 | spotty | turtle-spotted |
| 3 | 6 | tweety | bird-canary |
| 3 | 5 | spotty | turtle-spotted |
| 3 | 4 | mr.turtle | turtle-snapping |
| 3 | 3 | cadi | cat-persian |
| 3 | 2 | bonzo | dog-pitbull |
| 3 | 1 | dougie | dog-poodle |
| 4 | 2 | bonzo | dog-pitbull |
| 4 | 1 | dougie | dog-poodle |
| 4 | 6 | tweety | bird-canary |
| 4 | 5 | spotty | turtle-spotted |
| 4 | 4 | mr.turtle | turtle-snapping |
| 4 | 3 | cadi | cat-persian |
| 5 | 5 | spotty | turtle-spotted |
| 5 | 4 | mr.turtle | turtle-snapping |
| 5 | 3 | cadi | cat-persian |
| 5 | 2 | bonzo | dog-pitbull |
| 5 | 1 | dougie | dog-poodle |
| 5 | 6 | tweety | bird-canary |
+-----+------+-----------+-----------------+
30 rows in set (0.00 sec)
MariaDB [tmp]>
【讨论】:
感谢 Bernd,但在这个例子中我看不到你在哪里做 While 循环/迭代 序列引擎为您完成。如果您使用像“seq_from_to_step”这样的表名,它会创建一个 for 循环。您可以直接与 select * from seq_1_to_10 一起使用。我将编辑我的答案。 漂亮!由于我使用 Maria,这比其他任何方式都好。我正要接受它并为一件事编写一个存储过程......生成20个UUID。SELECT UUID() FROM seq_1_to_20
好多了!我真的应该阅读他们的其他引擎,也是。我可能错过了一些好东西
嗨,我参加聚会迟到了。我可以问一个问题吗?目前我有一个与 OP 类似的请求,但是我只有 MySQL 数据库的 Select 权限。是否可以使用 Mariadb 连接到它?是否有可能获得gui界面?谢谢!
您在回答中错过了先决条件:1) 将您的数据库从 MySQL 迁移到 Maria 2) 卸载 MySQL 3) 安装 Maria 4) 更新您自己的代码以使用 Maria 库进行连接 5) 测试您的软件新数据库 6) 修复不兼容性 7) 将新版本部署到服务器。终于可以使用序列引擎了!【参考方案3】:
这是不可能的。
我阅读了所有 MySQL 文档,它的句子只能在函数/过程体中声明。
【讨论】:
【参考方案4】:正如 Berd 所说,您可以使用内置序列来做到这一点,但这有点奇怪:
SET @i = 1;
set @str = 'a,b,c,d,e,f,g,h';
select temp.length into @length from
(select
ROUND(
(
LENGTH(dt.data)
- LENGTH( REPLACE (dt.data, ",", "") )
) / LENGTH(",")
)+1 AS length
from (select @str as data) dt
) temp;
SET @query = CONCAT('select substring_index(
substring_index(@str, '','', seq),
'','',
-1
) as letter from seq_', @i, '_to_',@length);
PREPARE q FROM @query;
EXECUTE q;
【讨论】:
【参考方案5】:DECLARE @count INT = 0;
WHILE @count < 10
BEGIN
DELETE TOP (300)
FROM EmployeeInformation
WHERE IsSynced=1 and CreatedDate<'2020-02-29'
SET @count = @count + 1;
END
这段代码对我有用。
【讨论】:
仅供参考,在 MySQL 中,您不能在存储例程之外使用DECLARE
。因此不是 OP 要求的。以上是关于在不使用存储过程的情况下循环 n 次的主要内容,如果未能解决你的问题,请参考以下文章
如何在不知道查询结果类型的情况下在 EF/VB.net 中运行存储过程?
在不创建存储过程的情况下,如何在 Oracle 中将多行连接成一行? [复制]