START TRANSACTION inside BEGIN ... END context or outside and LOOP 语法

Posted

技术标签:

【中文标题】START TRANSACTION inside BEGIN ... END context or outside and LOOP 语法【英文标题】:START TRANSACTION inside BEGIN ... END context or outside and LOOP syntax 【发布时间】:2012-08-28 21:19:10 【问题描述】:

我有两个关于 mysql 中的复合语句和事务的问题。

第一:

MySQL手册中有两处注释:

注意

在所有存储的程序中,解析器将 BEGIN [WORK] 视为 BEGIN ... END 块的开头。在此开始交易 上下文,请改用 START TRANSACTION。

注意

在所有存储程序(存储过程和函数、触发器、 和事件),解析器将 BEGIN [WORK] 视为 开始...结束块。在此上下文中使用 START 开始事务 而是 TRANSACTION。

我不明白具体是什么意思。他们的意思是我必须把START TRANSACTION 代替BEGIN 或紧跟在BEGIN 之后?

// 1st variant:

BEGIN
   START TRANSACTION
   COMMIT
END


// 2nd variant:

START TRANSACTION
COMMIT
END

哪个是正确的方法,第一个变体还是第二个变体?

第二:

我不想创建存储过程或函数。我只想在一般流程中创建一个带有循环的复合语句块,如下所示:

USE 'someDb';
START TRANSACTION
   ... create table statement
   ... insert statement

// now I want to implement some insert/select statements using loop, I do as follows:

DELIMITER $
BEGIN
  SET @n = 1, @m = 2;
  lab1: LOOP

   ... some insert, select statements here

   END LOOP lab1;
END $
DELIMITER ;

END

COMMIT

这样的结构可能吗?因为我抛出了一个错误:

Query: BEGIN SET @n = 1, @m = 2; lab1: LOOP SELECT ...
Error Code: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET @n = 1, @m = 2;
lab1: LOOP SELECT ...

我的问题是:

    是否允许在一般流程中使用BEGIN...END 而无需创建和使用存储过程或函数?

    是否允许在START TRANSACTION...COMMIT 内使用BEGIN...END,或者我必须在BEGIN...END 内使用START TRANSACTION...COMMIT

    BEGIN
       START TRANSACTION
       COMMIT
    END
    
    // vs.
    
    START TRANSACTION
       BEGIN
       END
    COMMIT
    

    如果我只想使用LOOP,我是否必须使用BEGIN...END?我可以只使用LOOP 语法而不启动BEGIN...END 吗? LOOP 手册中的唯一示例是:

      CREATE PROCEDURE doiterate(p1 INT)
         BEGIN
           label1: LOOP
             ... 
    

【问题讨论】:

【参考方案1】:

    是否允许在一般流程中使用 BEGIN...END 而无需创建和使用存储过程或函数?

    否:复合语句只能在存储程序体中使用。

    是否允许在START TRANSACTION...COMMIT 内使用BEGIN...END,或者我必须在BEGIN...END 内使用START TRANSACTION...COMMIT

    START TRANSACTION;COMMIT; 是单独的语句。如果您希望存储程序的主体包含多个语句,则需要将这些语句括在某种复合语句块中,例如 BEGIN ... END(这类似于将语句块括在大括号 ... 中类 C 语言)。

    也就是说,您可以拥有一个只包含单语句 START TRANSACTION;COMMIT; 的存储程序——这样的程序不需要任何复合语句块,而只会开始一个新的语句块/ 分别提交当前事务。

    在不允许复合语句块的存储程序之外,您可以在需要时发出 START TRANSACTION;COMMIT; 语句作为 &。

    如果我只想使用LOOP,我是否必须使用BEGIN...END?我可以只使用LOOP 语法而不启动BEGIN...END 吗?

    LOOP也是复合语句块,只在存储过程中有效。 没有必要LOOP 块包含在BEGIN ... END 块中,尽管这很常见(否则很难执行任何所需的循环初始化)。

在您的情况下,您显然希望将数据从循环结构插入到表中,您将需要:

定义一个你在其中使用LOOP的存储程序;

在外部程序中迭代一个循环,在每次迭代时执行数据库查询;或

根据 SQL 可以直接操作的集合重新定义您的逻辑。

【讨论】:

为什么循环只能存在于存储过程中?似乎迟缓必须创建一个临时存储的过程只是为了做一个简单的循环插入..... @Pacerier:因为这是属于您的应用程序代码的业务逻辑,而不是数据库层。 @eggyal 虽然我完全同意你的观点 Pacerier 有一个非常有效的观点,为什么 mysql 对于存储程序和标准查询的行为不同 @BobTheJanitor:SQL 是声明性的,因此没有诸如循环之类的过程语言结构。存储过程看起来像 SQL,但名称有点暴露了它:它们是过程性的,而不是声明性的(因此支持循环结构)。也就是说,人们可能希望通过过程循环完成的大多数行为都可以(并且应该)重写为声明性形式,以便可以用纯 SQL 表达,而无需任何 sproc。

以上是关于START TRANSACTION inside BEGIN ... END context or outside and LOOP 语法的主要内容,如果未能解决你的问题,请参考以下文章

realm数据库报错:Changing Realm data can only be done from inside a transaction.

为啥 START TRANSACTION 不会隐式影响自动提交

mysql:set autocommit=0与start transaction

mysql:set autocommit=0与start transaction

事务创建函数

mysql中set autocommit=0与start transaction区别