时间:2021-07-01 10:21:17 帮助过:6人阅读
该语句被作为一个原子性操作。对于增强VALUES
子句,还可以将其作为表值构造函数以标准方式构建一个派生表。
INSERT SELECT
也是作为原子性操作执行的。
使用INSERT EXEC
语句可以将存储过程或动态SQL批处理返回的结果集插入到目标表中。INSERT EXEC
非常类似INSERT SELECT
的语法和概念,只是将SELECT
语句替代为EXEC
语句。
--创建一个过程,创建过程和执行动态语句将在CHPATER 10中介绍
CREATE PROC Sales.usp_getorders
@country AS NVARCHAR(40)
AS
BEGIN
SELECT orderid, orderdate, empid, custid
FROM Sales.Orders
WHERE shipcountry = @country
END
--------------------------
INSERT INTO dbo.Orders(orderid, orderdate, empid, custid)
EXEC Sales.usp_getorders @country = ‘France‘;
SELECT INTO
是一个非标准的T-SQL语句,它使用查询的结果创建并填充目标表。“非标准”即不是ISO和ANSI SQL标准的一部分,不能使用此语句将数据插入到现有表中。
--比如备份Sales.Orders表中 France 的记录
SELECT orderid, orderdate, empid, custid
INTO Sales.FanceOrders
FROM Sales.Orders
WHERE shipcountry = ‘France‘
SELECT INTO
好处之一就是只要数据库的“恢复模式”属性未设置成完整
,SELECT INTO
就会以最小日志记录模式执行,意味着相对于完整日志记录这是一个非常快速的操作。更多详细住处请点击这里
可以使用BULK INSERT
语句将来自文件的数据插入到一个现在表中。在语句中指定目标表、源文件和选项。可以指定多个选项,包括数据文件类型(如:CHAR和NATIVE)、字段终止符、行终止符和其他所有文件选项。
--建表
IF OBJECT_ID(‘dbo.Orders‘, ‘U‘) IS NOT NULL DROP TABLE dbo.Orders;
CREATE TABLE dbo.Orders
(
orderid INT NOT NULL
CONSTRAINT PK_Orders PRIMARY KEY,
orderdate DATE NOT NULL
CONSTRAINT DFT_orderdate DEFAULT(SYSDATETIME()),
empid INT NOT NULL,
custid VARCHAR(10) NOT NULL
);
-------------------------------
BULK INSERT dbo.Orders FROM ‘d:\orders.txt‘
WITH
(
DATAFILETYPE = ‘char‘,
FIELDTERMINATOR = ‘,‘,
ROWTERMINATOR = ‘\n‘
);
GO
-------------------------------
/*
orders.txt 数据
10001,2009-02-12,3,1
10002,2009-02-12,5,2
10003,2009-02-13,4,2
10004,2009-02-14,1,1
10005,2009-02-13,1,3
10006,2009-02-15,3,3
10007,2009-02-15,2,2
10008,2009-02-15,1,3
10009,2009-02-16,2,3
10010,2009-02-16,3,1
*/
SQL SERVER 支持两种自动生成键的内置解决方案:标识列属性和序列对象(SQL SERVER 2012)。
SQL SERVER 允许无小数的任意数值类型的列定义了IDENTITY
的属性,此属性根据提供的种子值(第一参数)和增量值(步长值)自动生成值。通常情况下是使用此属性来生成SURROGATE
键(系统生成)。
CREATE table dbo.T1
(
keycol INT IDENTITY(1,1) CONSTRAINT PK_T1 PRIMARY KEY,
datacol VARCHAR(20) NOT NULL CONSTRAINT CHK_T1_datacol CHECK(datacol like ‘[A-Za-z]%‘)
)
--插入数据
INSERT INTO dbo.T1 VALUES (‘AAA‘),(‘BBB‘),(‘ccc‘)
-- 使用$identity 结果是1,2,3
SELECT $IDENTITY FROM dbo.t1
-- 显式值插入到dbo.T1里
SET IDENTITY_INSERT dbo.T1 ON
INSERT INTO dbo.T1(keycol,datacol) VALUES(4,‘dddd‘)
SET IDENTITY_INSERT dbo.T1 OFF
--结果是4,返回当前值,那么下一个值就是5
SELECT IDENT_CURRENT(‘dbo.T1‘)
IDENTITY
的值,还可以使用SQL SERVER提供的标识符$identity
。
序列对象是作为一种替代标识列的键生成机制添加到SQL SERVER 2012 的,这在其他数据库中已经实现的标准功能。
与标识列不同,序列对象的优点之一是它不是绑定到特定表中的特定列,而是作为数据库中的一个独立对象。当需要生成新值时,调用一个针对对象的函数,然后可以在任何地方使用返回值。
与标识列属性不同点:
序列对象可以指定任意整数数值类型(默认为BIGINT
)
序列对象支持指定数据类型内的最小值和最大值,如果不指定则为数据类型的最大或最小值。
序列对象可以循环(默认为不循环)。
/*
创建一个订单ID的序列对象,数据类型为INT,最小值为1,最大值为INT最大值,从1开始,步长为1.
*/
--创建
CREATE SEQUENCE dbo.SeqOrderIDs AS INT MINVALUE 1 CYCLE
--修改
ALTER SEQUENCE dbo.SeqOrderIDs NO CYCLE
--查询
SELECT (NEXT VALUE FOR dbo.SeqOrderIDs)
IF OBJECT_ID(‘dbo.T1‘, ‘U‘) IS NOT NULL DROP TABLE dbo.T1;
CREATE TABLE dbo.T1
(
keycol INT NOT NULL
CONSTRAINT PK_T1 PRIMARY KEY,
datacol VARCHAR(10) NOT NULL
);
--插入到T1
INSERT INTO dbo.T1 VALUES(NEXT VALUE FOR dbo.SeqOrderIDs, ‘b‘)
--获取序列对象的相关住处,可以查询sys.sequences的视图
SELECT current_value FROM sys.sequences WHERE name = ‘SeqOrderIDs‘
SQL SERVER扩展了对序列选项的支持,一个是类似于开窗函数的OVER
子句,在多行插入时来控制分配序列值的顺序。
INSERT INTO dbo.T1(keycol, datacol)
SELECT
NEXT VALUE FOR dbo.SeqOrderIds OVER (ORDER BY hiredate)
,LEFT(firstname,1) + left(lastname,1)
FROM HR.Employees
NEXT VALUE FOR
函数。这是超越了标识列属性的一个显著优势。
ALTER TABLE dbo.T1 ADD CONSTRAINT DF_T1_keycol
DEFAULT(NEXT VALUE FOR dbo.SeqOrderIDs)
FOR keycol
INSERT INTO dbo.T1(datacol) VALUES (‘TEST‘)
最后一个扩展是允许使用一个名为sp_sequence_get_range
的存储过程一次分配整个序列值范围。即如果你需要某个范围的序列值,最简单的方式就是仅更新序列一次,按照范围的大小递增。调用此过程时,设置相应的参数即可。
DECLARE @first SQL_VARIANT;
EXEC sys.sp_sequence_get_range
@sequence_name = ‘dbo.SeqOrderIDs‘,
@range_size = 1000,
@range_first_value = @firsts
T-SQL提供个删除表中的行的语句:DELETE和TRUNCATE。
准备表:
CREATE TABLE dbo.Customers
(
custid INT NOT NULL,
companyname NVARCHAR(40) NOT NULL,
contactname NVARCHAR(30) NOT NULL,
contacttitle NVARCHAR(30) NOT NULL,
address NVARCHAR(60) NOT NULL,
city NVARCHAR(15) NOT NULL,
region NVARCHAR(15) NULL,
postalcode NVARCHAR(10) NULL,
country NVARCHAR(15) NOT NULL,
phone NVARCHAR(24) NOT NULL,
fax NVARCHAR(24) NULL,
CONSTRAINT PK_Customers PRIMARY KEY(custid)
);
CREATE TABLE dbo.Orders
(
orderid INT NOT NULL,
custid INT NULL,
empid INT NOT NULL,
orderdate DATETIME NOT NULL,
requireddate DATETIME NOT NULL,
shippeddate DATETIME NULL,
shipperid INT NOT NULL,
freight MONEY NOT NULL
CONSTRAINT DFT_Orders_freight DEFAULT(0),
shipname NVARCHAR(40) NOT NULL,
shipaddress NVARCHAR(60) NOT NULL,
shipcity NVARCHAR(15) NOT NULL,
shipregion NVARCHAR(15) NULL,
shippostalcode NVARCHAR(10) NULL,
shipcountry NVARCHAR(15) NOT NULL,
CONSTRAINT PK_Orders PRIMARY KEY(orderid),
CONSTRAINT FK_Orders_Customers FOREIGN KEY(custid)
REFERENCES dbo.Customers(custid)
);
DELETE是一个标准语句,只包含有FROM
和WHERE
两个子句。
DELETE语句是完全日志记录的,所以,当删除大量的行时,运行时间可以会比较长一点。
TRUNCATE 语句删除表中的所有行,与DELETE 不同,TRUNCATE 不进行筛选的。TRUNCATE 是最小日志方式,所以性能较DELETE 要好得多。TRUNCATE 完全是事务性的(常见谅解),并且在ROLLBACK
时,SQL SERVER 可以撤销删除操作。
当表中含有标识列时,TRUNCATE 会重置标识值到原始的种子值,而DELETE 不会。
当目标表被外键约束引用时,即使父表为空,甚至外键是禁用的,都不允许使用TRUNCATE 语句。唯一解决方法就是删除该表的所有外键。
TRUNCATE 和 DROP 语句的执行速度都是非常快,为防止生产环境上发生误操作,可以创建一个虚拟表,带有指向生产表的外键,这样就可以防止用户从被引用表中删除数据或是删除表实体。
T-SQL 支持一个联接的非标准的DELETE 语法,联接本身是有筛选作用的。
DELETE FROM o
FROM dbo.Orders o INNER JOIN dbo.Customers c ON o.custid =c.custid
WHERE c.country = ‘USA‘
其实这个语句和SELECT
语句样的。你把DELETE
替换成SELECT *
毫无专程感。
如果想要使用标准语句来实现这个功能,可以这样
SQL SERVER 很有可能以相同的方式处理这两个查询,因此,不用考虑两者之间的性能差异。但是,建议尽可能的坚持标准。
T-SQL 也支持基于联接的UPDATE 非标准语句。
与DELETE 和 SELECT 差别不大
UPDATE od SET od.discount += 0.05
FROM Sales.OrderDetails od
INNER JOIN dbo.Orders o ON o.orderid = od.orderid
WHERE o.custid = 1
当然也也可以使用标准SQL,通过子查询来进行UPDATE
。
在某些情况下,联接版本比子查询版本会有性能优势。除了筛选之外,联接也为用户提供了访问其他表属性的权限,用户可以在SET
子句的列赋值中使用这些属性。
下面是非标准UPDATE语句:
UPDATE T1
SET col1 = T2.col1,
col2 = T2.col2,
col3 = T2.col3
FROM dbo.T1 JOIN dbo.T2
ON T2.keycol = T1.keycol
WHERE T2.col4 = ‘ABC‘;
需要UPDATE
的T1
表可以直接引用联接查询的T2
表的列。如果我们使用标准SQL,可能需要使用的子查询就会比较多,造成SQL比较繁杂:
UPDATE dbo.T1
SET col1 = (SELECT col1
FROM dbo.T2
WHERE T2.keycol = T1.keycol),
col2 = (SELECT col2
FROM dbo.T2
WHERE T2.keycol = T1.keycol),
col3 = (SELECT col3
FROM dbo.T2
WHERE T2.keycol = T1.keycol)
WHERE EXISTS
(SELECT *
FROM dbo.T2
WHERE T2.keycol = T1.keycol
AND T2.col4 = ‘ABC‘);
标准SQL支持行构造函数(也称为矢量表达式),在SQL SERVER 2012中只实现了部分功能,行构造函数的许多方面SQL SERVER还未实现。包括下面的UPDATE中的SET
子句:
UPDATE dbo.T1
SET (col1, col2, col3) =
(SELECT col1, col2, col3
FROM dbo.T2
WHERE T2.keycol = T1.keycol)
WHERE EXISTS
(SELECT *
FROM dbo.T2
WHERE T2.keycol = T1.keycol
AND T2.col4 = ‘ABC‘);
PS: 虽然本书作者说要使用标准SQL来进行UPDATE
或DELETE
,但是实际使用中笔者认为若是对生产环境中的表操作时还是使用T-SQL的联接查询好一点。
理由如下:
DELETE FROM Sales.Customers
SELECT *
FROM Sales.Customers
WHERE custid = 28
上述语句本来想删除custid = 28
的这个用户,但是删除前一般都会确认条件是否正确,所以可能会使用SELECT
进行查询,但是有时手滑或者不注意直接运行上述语句,Sales.Customers
的数据会被全部删掉。如果使用T-SQL的联接来进行删除可以这样
DELETE FROM c
SELECT *
FROM Sales.Customers
WHERE custid = 28
SELECT
查询后再对Sales.Customers
起别名c
,再注释掉SELECT
,就不会有误操作了。