毋庸置疑,开发者最常用的数据库技术就是 SQL 了,即便是 ORM 大行其道的今天也常常需要写 SQL 语句。而 SQL 语句中最常用的就是增删改查了,本系列就先对增删改查语句来个系统的回顾吧!

1、插入语句 INSERT INTO

1.1、用 INSERT 插入单行数据

INSERT INTO 的作用是向表中添加新行,语法如下:

INSERT INTO table-name(column1,column2,...column-n) VALUES(value1,value2,...value-n);

譬如要向好学生表中添加 1 条数据,示例如下:

INSERT INTO T_GoodStudents(Name,Birthday) VALUES('李尔','1990-01-09'); -- 显示指定要插入字段

如果按表中的字段顺序给出全部字段的值,那么就不用显示指定字段了,示例如下:

INSERT INTO T_GoodStudents VALUES(1,'邱晨',1,'1990-09-01');

1.2、用 INSERT 插入多行数据

INSERT INTO 还可以一次向表中添加多条数据,如要一次性向学生表中添加 3 条数据,示例如下:

INSERT INTO T_GoodStudents(Name,Gender,Birthday)
VALUES('张三',1,'1993-03-03'),('李四',1,'1994-04-04'),('王五',1,'1995-05-05');

注意:在插入全部字段时,插入多行数据也可以像插入单行数据那样省略字段列表,但必须确保各行之间的数据个数相同类型兼容

1.3、用 INSERT 插入子查询结果行

向表中插入数据时,既可以通过 VALUES 子句显示地列出插入值,也可以通过 SELECT 子句来获得插入值。语法如下:

INSERT INTO target-table-name SELECT columns FROM source-table-name;

该语句的效果类似于把一张表的数据复制到另一张表,要复制的字段和行都可以显示的指定。当要将大量行从源表传输到目标表中时,该语句还能够以最小日志记录的方式高效的完成。示例如下:

INSERT INTO T_GoodStudents SELECT Id,Name,Gender,Birthday FROM T_Students;   -- 完全复制(数据)
INSERT INTO T_GoodStudents(Name,Gender) SELECT Name,Gender FROM T_Students; -- 指定部分字段复制
INSERT INTO T_GoodStudents(Name) SELECT Name FROM T_Students WHERE Gender=1; -- 指定部分数据复制

如果目标表和源表的表结构相同,子查询的字段列表还可以用 * 来代替。在指定字段复制时,无需表结构相同,只要对应字段的数据类型兼容即可,甚至还可以没有源表,一个子查询就够了。示例如下:

INSERT INTO T_GoodStudents SELECT 999,'李敏',0,'1991-02-02'; -- 插入 1 条(来自子查询的)数据

INSERT INTO T_GoodStudents(Id,Name,Birthday)
SELECT 11,'王阳','1991-03-02' UNION ALL
SELECT 12,'李玉','1991-07-02' UNION ALL
SELECT 13,'郑爽','1991-02-02'; -- 插入 3 条(来自子查询的)数据

1.4、INSERT 小结及特殊字段插入方法

在使用 INSERT INTO 语句向表中插入新行时,除了带默认值和带标识的字段,其它必填的字段都需要显示的给出值,而非必填字段不给值时 SQL Server 默认会给它一个 NULL 值,也可以显示的给定一个 NULL 值。

1.4.1、将数据插入有默认值的字段中 时,如果没有为指定了默认值的字段指定值,那么新行的该字段的值将会是默认值。假如要添加一行,有默认值的字段就让它为默认值,没有默认值的字段就让它为 NULL,那么就可以用如下语句:

INSERT INTO T_GoodStudents DEFAULT VALUES;

1.4.2、将数据插入到标识列中 时,无论是指定插入字段还是不指定插入字段,都无需考虑标识列,因为 SQL Server 的关系引擎会根据标识增量和标识种子自动为标识列赋值。如果需要为标识列指定值,就需要先把 IDENTITY_INSERT 打开,然后才能插入,示例如下:

SET IDENTITY_INSERT T_Students ON;                  -- 当前会话有效,别的会话不受影响
INSERT INTO T_Students(Id,Name) VALUES(-1,'李哈哈'); -- Id 字段为标识列

注意1:必须在 INTO 子句中显示列出标识列,否则即便在 VALUES 子句中提供所有字段的值也还是会报错。

注意2:如果想在当前会话中继续像默认情况那样忽略标识列,就需要把 IDENTITY_INSERT 关掉,示例如下:

SET IDENTITY_INSERT T_Students OFF;

2、删除语句 DELETE

2.1、用 DELETE 删除表中指定行

DELETE 语句用于从表中删除现有行,语法如下:

DELETE FROM table-name WHERE delete-conditions;

WHERE 子句的作用在于确定删除哪些行,示例如下:

DELETE FROM T_GoodStudents WHERE Id >= 20;                             -- 删除 Id 大于等于 20 的数据
DELETE FROM T_GoodStudents WHERE Id NOT IN(SELECT Id FROM T_Students); -- 删除 Id 不在学生表中的数据

注意:在 PL/SQL 中可以方便的给要删数据的表取个别名,以便限定 WHERE 子句中的字段,但在 T-SQL 中却不能直接给 DELETE 语句中要删数据的表取别名。如果想要限定删除条件中的字段,可以用如下两种写法:

DELETE FROM T_Students WHERE T_Students.Id = 4;     -- 直接用表名来限定(条件字段少时比较方便)
DELETE T_Students FROM T_Students t WHERE t.Id = 5; -- 在 DELETE 子句中加上表名(条件字段多时更方便)

理论上 DELETE 语句是可以不带 WHERE 子句的,但这个操作很危险,因为它意味着删除表中所有行。

2.2、用 TRUNCATE TABLE 高效清空表

TRUNCATE TABLE 用于删除表中的所有行,如果表中有标识列,标识列会重新开始计数,相当于清空了整个表。语法如下:

TRUNCATE TABLE table-name;

如要清空好学生表,示例如下:

TRUNCATE TABLE T_GoodStudents;

注意:尽管不带 WHERE 条件的 DELETE 语句就可以删除表中所有数据,但 TRUNCATE TABLE 比 DELETE 的速度更快,使用的系统资源和事务日志资源也更少。

3、更新语句 UPDATE

UPDATE 语句用于更新指定表中的现有数据,语法如下:

UPDATE table-name
SET column1 = value1,column2 = value2,...column-n = value-n
WHERE update-conditions;

WHERE 子句用于限定哪些行需要被更新,如果不带 WHERE 子句就会更新所有行,当然这很危险,一般也没有这种需求。可以一次更新一个字段,也可以一次更新多个字段,字段的值可以显示给出,也可以是个表达式,表达式中还可以引用表中的字段。示例如下:

UPDATE T_GoodStudents SET Name = '王娜' WHERE Id = 7;            -- 更新一个字段的值
UPDATE T_GoodStudents SET Name = '徐莉',Gender = 0 WHERE Id = 7; -- 更新多个字段的值
UPDATE T_GoodStudents SET Birthday = GETDATE()-10 WHERE Id = 7; -- 用表达式给字段赋值
UPDATE T_GoodStudents SET Birthday = Birthday-10 WHERE Id = 7; -- 在表达式中引用字段
UPDATE T_GoodStudents SET Name += '学生' WHERE Id > 3; -- 在姓名后面加上"学生"

3.1、SET 子句内包含子查询时,示例如下(把班级名更新到学生备注中):

UPDATE T_Students SET Remark = (SELECT t.Name FROM T_Classes t WHERE t.Id = ClassId);

注意1:上例中没有 WHERE 子句,这意味着(不论学生表中的 ClassId 是否在班级表中出现过)都会更新整个学生表,ClassId 未在班级表中出现过的学生备注会被更新为 NULL。尽管看似简单,但笔者就曾在职场中多次遇到工作数年的技术人员因忽略这点而误改了数据。

注意2,如果恰好两个表中的关联字段名相同,大概率上会出问题或报错,为了稳妥起见需要限定一下字段。在 Oracle 中可以方便的通过表别名来限定,然而 SQL Server 却不支持给 UPDATE 语句的 UPDATE 子句中的表取别名,但可以直接通过表名来限定字段。示例如下:

UPDATE T_GoodStudents
SET Name = (SELECT t.Name FROM T_Students t WHERE t.Id = T_GoodStudents.Id)
WHERE T_GoodStudents.Id IN(SELECT Id FROM T_Students); -- 将学生表的姓名同步到好学生表

3.2、WHERE 子句内含子查询时,示例如下(将单科考试 3 次不及格的写入到学生备注中):

UPDATE T_Students SET Remark = '单科3次不及格'
WHERE Id IN(
SELECT t.StudentId
FROM T_ExamResults t
WHERE t.Scores < 60
GROUP BY t.StudentId,t.CourseId HAVING COUNT(1) >= 3
);

3.3、带 FROM 子句的 UPDATE 语句,示例如下(把所有学生最近一次考试的总成绩更新到学生备注中):

UPDATE T_Students SET Remark = t2.SumScore
FROM T_Students t1
JOIN(
SELECT t.StudentId,SUM(t.Scores) SumScore
FROM T_ExamResults t
WHERE t.Counts = (SELECT MAX(Counts) FROM T_ExamResults)
GROUP BY t.StudentId
) t2
ON t1.Id=t2.StudentId;

如果只需要更新部分学生,比如仅更新 1 班的学生,就可以在 ON 后面直接加AND t1.ClassId=1,或者在整个语句后面加WHERE t1.ClassId=1。有意思的是,这种 UPDATE 语句即便没有 WHERE 条件,也不会对未在 FROM 子句中限定的行产生影响。

4、合并语句 MERGE

相比较 INSERT、DELETE、UPDATE 和 SELECT 来说,MERGE 出现的要晚一些,但也有十多年了,各大 SQL 数据库在 21 世纪头几年陆续提供了对 MERGE 的支持。简单来说,MERGE 语句就是对增删改查的“合并”,使得可以在一个语句内根据查询的匹配情况来决定是否要增、删或改某些数据,而不必再写冗长的逻辑判断和事物处理了。语法如下:

MERGE target-table-name
USING source-table-expressions ON merge-search-conditions
WHEN MATCHED AND clause-search-conditions THEN merge-matched
WHEN NOT MATCHED AND clause-search-conditions THEN merge-not-matched;

使用 MERGE 在单个语句中对表执行 INSERT 或 UPDATE 操作,示例如下:

MERGE T_Students AS target
USING(SELECT '朱丹丹',0) AS source (Name,Gender) ON(target.Name = source.Name)
WHEN MATCHED THEN
UPDATE SET Gender = source.Gender
WHEN NOT MATCHED THEN
INSERT(Name,Gender) VALUES(source.Name,source.Gender);

使用 MERGE 在单个语句中对表执行 INSERT、DELETE 或 UPDATE 操作,示例如下:

MERGE T_Students AS target
USING(SELECT '刘天宝',1,'1990-09-09') AS source (Name,Gender,Birthday)
ON(target.Name = source.Name)
WHEN MATCHED AND target.Birthday < source.Birthday THEN
DELETE
WHEN MATCHED THEN
UPDATE SET target.Gender = source.Gender,target.Birthday = source.Birthday
WHEN NOT MATCHED THEN
INSERT(Name,Gender,Birthday) VALUES(source.Name,source.Gender,source.Birthday);

5、用 TOP 参数限制受影响的行

熟悉 SQL Server 的开发者估计都知道 TOP 参数可以用来限制查询语句的返回行数,但其实 TOP 参数不仅可以限制 SELECT 的结果集,还以限制受 INSERT、DELETE 或 UPDATE 影响的行。

5.1、带 TOP 参数的 INSERT 语句,示例如下(随机将 3 个女学生添加到好学生表):

INSERT TOP(3) INTO T_GoodStudents
SELECT t.Id,t.Name,t.Gender,t.Birthday FROM T_Students t WHERE t.Gender = 0;

如果想要按某种特定的顺序插入数据,譬如要把年龄最大的 3 个学生添加到好学生表,示例如下:

INSERT INTO T_GoodStudents
SELECT TOP(3) t.Id,t.Name,t.Gender,t.Birthday FROM T_Students t ORDER BY t.Birthday;

5.2、带 TOP 参数的 DELETE 语句,示例如下(随机删除 3 个女学生):

DELETE TOP(3) FROM T_GoodStudents WHERE Gender = 0;

如果想要按某种特定的顺序删除数据,譬如要删除年龄最大的 3 个学生的信息,示例如下:

DELETE FROM T_GoodStudents
WHERE Id IN(SELECT TOP(3) t.Id FROM T_GoodStudents t ORDER BY t.Id DESC);

5.3、带 TOP 参数的 UPDATE 语句,示例如下(随机将 3 个男学生的性别更新为 0):

UPDATE TOP(3) T_Students SET Gender = 0 WHERE Gender = 1;

如果想要按某种特定的顺序更新数据,譬如要将年龄最大的 3 个男学生的性别更新为 0,示例如下:

UPDATE T_GoodStudents SET Gender = 0
FROM(SELECT TOP(3) t1.Id FROM T_GoodStudents t1 ORDER BY t1.Id DESC) t2
WHERE T_GoodStudents.Id = t2.Id;

6、用 OUTPUT 子句返回受影响的数据

试想一下,如果需要在插入的一条数据的同时返回这条数据,或者在删除一条数据的同时备份这条数据,我们当然可以用多条简单语句来共同完成,并且通过事务来确保操作的原子性。但其实这类需求可以通过 OUTPUT 子句来更好的完成,而且一个语句就能搞定,不必加事务,因为它本身就具备原子性。

在使用 OUTPUT 返回数据时,需要借助 INSERTED 或 DELETED 来引用字段值。INSERTED 用来引用插入操作或更新操作添加的值,DELETED 用来引用删除操作或更新操作删除的值。在 INSERT 语句中不能访问 DELETED,在 DELETE 语句中不能访问 INSERTED,在 UPDATE 语句中两个都能访问。示例如下:

INSERT T_GoodStudents OUTPUT inserted.* VALUES(7,'高鹏',1,'1979-11-11'); -- 插入 1 条信息并输出
DELETE TOP(1) FROM T_GoodStudents OUTPUT deleted.Id,deleted.Name; -- 删除 1 条信息并输出 UPDATE TOP(2) T_GoodStudents SET Gender = 1
OUTPUT deleted.Name,inserted.Name,deleted.Gender,inserted.Gender; -- 更新 2 条信息并输出

还可以结合 INTO 把 OUTPUT 返回的数据插入到另一张表中,示例如下:

INSERT T_GoodStudents OUTPUT inserted.* INTO T_GoodStudents VALUES(9,'黄强',1,'1999-11-11');
DELETE TOP(1) FROM T_GoodStudents OUTPUT deleted.* INTO T_GoodStudents;
UPDATE TOP(2) T_GoodStudents SET Gender = 1 OUTPUT deleted.* INTO T_GoodStudents;

7、本文小结

本文主要讲述了 T-SQL 语句中的 INSERT、DELETE、UPDATE 和 MERGE 共 4 个 DML 语句及其子句,以及一个 DDL 语句 TRUNCATE TABLE,而且这几个语句都是实际开发中特别常用的语句。

在 Oracle 中总是给表取别名是个很好的习惯,但 SQL Server 的增删改语句均不支持对目标表取别名,只有合并语句和查询语句支持别名。不过 SQL Server 中的所有 DML 语句都支持用表名来限定字段名。

有些读者可能会有疑问“为什么 SQL Server 管理工具生成的语句总是要给对象名前后加上中括号?”。尽管不好看,但的确有道理,因为它可以防止用户自定义名称跟系统关键字冲突。譬如你要用 USER 做表名或字段名,就得用中括号包裹一下。另外,如果想用某些特殊符号来命名也需要用中括号包裹,但一般不建议这么做,太变态了!

如果你不幸遇到头尾带空格的对象名,你会发现只写空格以外的名称部分是访问不到该对象的,这种情况也可以用中括号来解决。如果你有修改权限的话建议还是把空格删掉吧,太恶心了!假如学生表前后有空格,查询示例如下:

SELECT * FROM [ T_Students ];

本文参考链接:

去导航目录篇下载创建本系列博文通用库表及数据的 SQL 语句

本文链接http://www.cnblogs.com/hanzongze/p/tsql-crud.html

版权声明:本文为博客园博主 韩宗泽 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!个人博客,能力有限,若有不当之处,敬请批评指正,谢谢!

SQL Server温故系列(1):SQL 数据操作 CRUD 之增删改合的更多相关文章

  1. SQL Server温故系列(0):导航目录

    创建本系列博文通用库表及数据的 SQL 语句:下载 SQL Server温故系列(0):导航目录 SQL Server温故系列(1):SQL 数据操作 CRUD 之增删改合 SQL Server温故系 ...

  2. SQL Server温故系列(2):SQL 数据操作 CRUD 之简单查询

    1.查询语句 SELECT 1.1.查询语句的 SELECT 子句 1.2.查询语句的 FROM 子句 1.2.1.内连接查询 INNER JOIN 1.2.2.外连接查询 OUTER JOIN 1. ...

  3. SQL Server温故系列(3):SQL 子查询 & 公用表表达式 CTE

    1.子查询 Subqueries 1.1.单行子查询 1.2.多行子查询 1.3.相关子查询 1.4.嵌套子查询 1.5.子查询小结及性能问题 2.公用表表达式 CTE 2.1.普通公用表表达式 2. ...

  4. SQL Server温故系列(5):SQL 查询之分组查询 GROUP BY

    1.GROUP BY 与聚合函数 2.GROUP BY 与 HAVING 3.GROUP BY 扩展分组 3.1.GROUP BY ROLLUP 3.2.GROUP BY CUBE 3.3.GROUP ...

  5. SQL Server温故系列(4):SQL 查询之集合运算 & 聚合函数

    1.集合运算 1.1.并集运算 UNION 1.2.差集运算 EXCEPT 1.3.交集运算 INTERSECT 1.4.集合运算小结 2.聚合函数 2.1.求行数函数 COUNT 2.2.求和函数 ...

  6. 设置Sql server用户对表、视图、存储过程、架构的增删改查权限

    根据数据库Schema限制用户对数据库的操作行为 授予Shema dbo下对象的定义权限给某个用户(也就是说该用户可以修改架构dbo下所有表/视图/存储过程/函数的结构) use [Your DB N ...

  7. 使用Red Gate Sql Data Compare 数据库同步工具进行SQL Server的两个数据库的数据比较、同步

    Sql Data Compare 是比较两个数据库的数据是否相同.生成同步sql的工具. 这一款工具由Red Gate公司出品,我们熟悉的.NET Reflector就是这个公司推出的,它的SQLTo ...

  8. SQL Server时间粒度系列----第9节时间粒度示例演示

    本文目录列表: 1.准备测试数据 2.向测试数据表添加相关时间粒度字段列 3.基于日月季年统计汇总的演示 4.总结语 5.参考清单列表   准备测试数据   为了提供不同时间粒度示例的演示,就需要测试 ...

  9. SQL Server调优系列基础篇

    前言 关于SQL Server调优系列是一个庞大的内容体系,非一言两语能够分析清楚,本篇先就在SQL 调优中所最常用的查询计划进行解析,力图做好基础的掌握,夯实基本功!而后再谈谈整体的语句调优. 通过 ...

随机推荐

  1. ZOJ 2319 Beatuiful People(单调递增序列的变形)

    Beautiful People Time Limit: 5 Seconds      Memory Limit: 32768 KB      Special Judge The most prest ...

  2. 转义及编码(\u, \x)

    首先前面的 \表示转义, \x:只是 16 进制的意思,后边跟两位,则表示单字节编码: \d:十进制:\o:八进制: 对于 \xaa ⇒ chr(0xaa) ⇒ chr(16*a+a) \u:unic ...

  3. Program for Linux USB-devices driver step by step (ONE)

    Program for Linux USB-devices driver 開始啃硬骨头~ 这里我打算一步步给出USB device driver 的demo.希望有心能可以共同交流学习. 希望认识很多 ...

  4. cocos2D-X从的源代码的分析cocos2D-X学习OpenGL(1)----cocos2D-X渲染架构

     个人原创.欢迎转载,转载请注明原文地址http://blog.csdn.net/bill_man 从本篇文章開始,将分析cocos2D-X 3.0源码,第一部分是从cocos2D-X学习OpenGL ...

  5. mysql常见操作汇总 专题

    mysql中in多个字段 1. 基本用法 SELECT * FROM USER WHERE , , ); 2. 多个字段同时使用 SELECT * FROM USER WHERE (, ),(, ), ...

  6. 所有语言的Awesome(2)

    Curated list of awesome lists https://awesomeweekly.co https://github.com/sindresorhus/awesome ✨ Pre ...

  7. 获取同时间段不同的时间 php

    /** * 根据指定日期返回经过的年月 * @param string $sDay 开始日期 * @param string $eDay 结束日期 * @returnse multitype:stri ...

  8. python 运行出现flask运行时提示出错了或者报服务器出错,ValueError: View function did not return a response

    python manage.py runserver -d

  9. SpringMvc 资料

    web.xml解释 http://www.cnblogs.com/superjt/p/3309255.html url-pattern解释 http://www.cnblogs.com/zhangpe ...

  10. 8086 CPU 寄存器简介(超详细,图文并茂)

    http://www.cnblogs.com/BoyXiao/archive/2010/11/20/1882716.html