你总是常听到的SQL批语句 | 数据库教程4:关于批语句

846 阅读6分钟

似乎批SQL语句和DBMS中的批处理并不是完全相同的概念。关于这方面暂未找到特别明确的介绍。

批处理

在标准SQL中,并没有批的概念。几乎只在SQL Server中对批SQL语句(Batches of SQL Statements)有比较清晰的介绍。

一批SQL语句是两个或多个SQL语句为一组或与两个或多个SQL语句组成的组具有相同效果的单个SQL语句。

简单讲就是多个SQL语句组成的一组语句,作为整体提交给RDBMS执行。

最常见的,也是几乎所有数据库管理系统都支持的存储过程(Stored Procedure),如果包含多个SQL语句,也被认为是一批SQL语句。

将多个SQL语句放在一起执行,也被认为是批处理。可以说这是显式的批SQL语句。

同样,放在一个SQL文件中的多条SQL语句,当直接加载SQL脚本文件并运行时,也可以被看做是批处理语句。甚至SQL文件中包含多个批。

Insert的多行插入语句,也是一种批处理,虽然只有一句,但是却是插入多行。

INSERT INTO <table> (<col1>, <col2>...)  
   VALUES (?, ?...),(?, ?...)...;  

类似的,批量更新(update)、删除(delete)等,也属于批量SQL语句。

SSMS中的工具命令GO

在SQL Server Management Studio(SSMS)中,支持使用GO语句,作为一批T-SQL语句的结束信号。这也是很多SQl Server示例中会包含GO语句的原因。

GO分割的SQL批处理语句之间是相对独立的。在一个批中执行成功或失败(报错),不影响另一个批语句的执行。

GO不是Transact-SQL语句,它是只能由sqlcmd、osql工具或SSMS编辑器识别的,作为批标志将当前的一批SQL语句发送给RDBMS执行的工具性的语句或符号,或者它是作为批命令被识别的。

因此有的地方将GO称作批分隔符,而不是一个语句。但是GO后面可以跟执行的次数,因此可以称为工具命令。

GO自身从不会发送给SQL Server服务器(执行)。

在SSMS中也可以改为其他符号( Tools -> Options -> Query Execution)。

GO必须单独作为一行出现。

GO [count]用于指定GO之前的批处理执行的次数。

一个批处理被编译为单个执行计划(execution plan),也因此,一次一个批语句。

批语句中的错误处理

批语句中的错误处理要依照不同的错误类型。

语法错误

批SQL语句是作为单个编译单元处理的,因此,批处理中的语法错误,将会阻止批语句中任何语句的执行。

语法错误,批中所有语句都不会执行。

SELECT 'Batch - Start';
SELEC 'Batch - Middle';
SELECT 'Batch - Start';

SQL Server中报错:

Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'SELEC'.

PostgreSQL中报错:

ERROR: 错误:  语法错误 在 "SELEC" 或附近的
LINE 2:   SELEC 'Middle';

DDL的运行时错误会终止批处理

执行批时,如果DDL语句发生运行时错误,则批中剩余的语句不会执行:

INSERT INTO Productcopy VALUES ('0011', 'T恤' ,'衣服', 1000, 500, '2009-09-20');
-- Productcopy数据库存在因此运行时报错。后面的语句不会执行
CREATE TABLE Productcopy
(product_id      CHAR(4)      NOT NULL,
 product_name    VARCHAR(100) NOT NULL,
 product_type    VARCHAR(32)  NOT NULL,
 sale_price      INTEGER ,
 purchase_price  INTEGER ,
 regist_date     DATE ,
 PRIMARY KEY (product_id)
);
INSERT INTO Productcopy VALUES ('0012', 'T恤' ,'衣服', 1000, 500, '2009-09-20');

SQL Server中报错,并且后面的语句不会执行插入:

Msg 2714, Level 16, State 6, Line 2
There is already an object named 'Productcopy' in the database.

在PostgreSQL的pgAdmin4中执行上面的多个语句。会报错"productcopy"已经存在,同时所有语句的执行都会回退到执行前的状态。这是因为在pgAdmin4中,多个SQL会作为一个事务进行处理。

而在psql命令行中,又会被拆分为单独的语句,每条语句自动提交事务。错误"productcopy"存在,但是前后的语句也都执行插入。

违反约束的运行时错误仅终止当前语句

违反约束的错误仅停止当前语句的执行,批中剩余的语句仍会执行。

-- 当前语句违反主键约束,但仅会停止当前语句的执行
INSERT INTO Productcopy VALUES ('0011', 'T恤' ,'衣服', 1000, 500, '2009-09-20');
INSERT INTO Productcopy VALUES ('0012', 'T恤' ,'衣服', 1000, 500, '2009-09-20');

SQL Server报错,当前语句终止执行,后面的语句仍会运行:

Msg 2627, Level 14, State 1, Line 1
Violation of PRIMARY KEY constraint 'PK__Productc__47027DF52594C47B'. Cannot insert duplicate key in object 'dbo.Productcopy'. The duplicate key value is (0011).

在PostgreSQL的pgAdmin4和psql命令行中,与上一部分的情况一样。

SQL Server中批命令的限制

  1. 一个批中可以有多个CREATE TABLECREATE INDEX语句。

  2. 但是CREATE VIEWCREATE FUNCTIONCREATE PROCEDURECREATE TRIGGER语句在批中必须作为唯一的语句,而不能多个一起存在于一个批中。

如下,一个批中有两个创建视图的语句:

 -- Only one CREATE VIEW can be in a batch
CREATE VIEW Product_view1 AS SELECT * FROM Productcopy;
CREATE VIEW Product_view2 AS SELECT * FROM Product;

执行时报错如下:

Msg 156, Level 15, State 1, Procedure Product_view1, Line 2 [Batch Start Line 0]
Incorrect syntax near the keyword 'CREATE'.
  1. 一个批中的本地变量在另一个批中不可用。

  2. 在一个批中,执行存储过程,必须使用EXECUTE(或EXEC)关键字,除非是批中的第一个语句。

批、语句和RPC

一个SQL批是一个或多个语句作为一组的集合,使用GO语句分割批。

一个单独的可执行的SQL命令(一行或分号;分割),为一个SQL语句。

RPC调用是来自客户端应用程序到数据库的调用。windows服务、web应用程序或其他应用程序,任何连接到数据库的应用实际都是进行一个RPC调用。

在Profiler(SSMS)中,可以看到到达数据库服务器的所有内容。来自Management Studio的批处理、来自外部应用程序的RPC调用(可以是批处理调用或存储过程调用)、来自Management Studio的过程执行(procedure execution)。

它们都是有T-SQL语句组成。

此部分出自Difference between SQL batch, statement and RPC?

参考