1.认识select
select的主要语法如下,这个很重要因为只有记住了整体的结构才能应对任何情况。从中可以看到select的强大主要就是建立在where、group by、having、order by这4个功能之上。
select [all | distinct] select_list [into new_table] [from table_source]
[where search_condition]
[group by byexpression]
[having search_condition]
[order by order_expression [asc|desc] ]
再来看这6个关键字的执行顺序,显然首先你得找到表取得最原始最庞大的数据,因此是from。接着是where用来进行过滤,然后group by和having开始执行,这样数据的过滤已经完成所以就是select了,还有一个order by就是最后了。总结起来顺序是from、where、group by、having、select、order by。如果再加上其它功能点,完整的执行顺序为from,on,join,where,group by,having,with rollup或cube,select,distinct,order by,top。关于from要注意当为表使用别名时,一旦使用将不能够再用表名,只能使用as后的别名。where中可以使用很多运算符组合过滤条件,系统过滤数据时将按照过滤条件以行为单位一条条的进行过滤。在运算符中逻辑运算符的优先级为not、and、or,而且not只能用于简单条件式,不能用于包含and和or的复杂条件式。
group by为我们带来了非常方便的分组功能,但要注意使用时select中只能是by后面的列名和聚集函数。当以by后面的列进行分组时,如果在select中包括其它的列比如columnA,那么系统将不知道显示分组后的columnA集合中的哪一条数据,这就是为什么不能select其它列的原因。另外还要注意group by后的列名必须使用完整的名字,不能出现as的别名,原因很简单就是因为执行顺序。having和where很相似,它们都可以对结果进行过滤。它们的区别则有2点:having中可以包含聚集函数,而where是不允许有聚集函数的;having中出现的列必须是select中存在的,而where则可以使用表中的任意列。
在查询中,如果要对多表进行查询可使用where或表连接来进行关联。和表连接一样,联合查询也是查询中很有效的手段,当然它和表连接有着本质的区别,使用它有3个要注意的地方。首先union联合的2个select语句,必须要有相同数量的列。在有相同列的情况下,列还必须拥有相似的数据类型,最后select语句的顺序也要相同。它的作用则是将多个select的结果集拼接在一起并一起显示在结果集上,表连接是将表进行关联连接,而联合查询只是联合了查询出来的结果集,将这些结果集放到一起显示而已。使用union会发现它除了去除重复行外,还会对结果集进行一个排序,union all既不会去除重复行也不会对结果进行排序。关于union的用法读者可自己去写sql,这里我要强调的是一个使用union常见的错误,数据表sql语句如下所示。
--创建数据库
create database testDb on
(
name=testDb_data,
filename='D:\testDb_data.mdf',
size=4,
filegrowth=10%
)
log on
(
name=testDb_log,
filename='D:\testDb_data.log',
size=2,
filegrowth=10%
) --创建student表
use testDb
create table student
(
studentId int primary key,
studentName nvarchar(16) not null,
studentAge int default(18),
studentSex nchar(1)
) create table teacher
(
teacherId int primary key,
teacherName nvarchar(16) not null,
teachClass nvarchar(16)
) --为student添加新的一列并添加主外键约束
alter table student add
teacherId int,constraint teacher_FK foreign key(teacherId) references teacher(teacherId)
select * from teacher insert into teacher values(1,'刘老师','安卓')
insert into teacher values(2,'吴老师','网站')
insert into teacher values(3,'王老师','物联网') select studentId,studentName from student
union all
select teacherId,teacherName from teacher
如果在上面sql中的union后加上order by teacherId则会出现错误,提示上说order by项必须出现在选择列表中,可是这里teacherId明明就是选择列表中的列啊。再仔细看看结果集会发现列上的名字是以第一个select中的列名为标题的,因此这里teacherId还真没有出现选择列表中。将teacerId改为studentId或studentName将会顺利执行,关于结果集读者可自行去试。
select studentId,studentName from student
union all
select teacherId,teacherName from teacher
order by teacherId
select studentId,studentName from student
union all
select teacherId,teacherName from teacher
order by studentId,studentName
在错误提示中还出现了intersect和except运算符,既然遇到了那肯定要掌握。这3个运算符一起出现说明它们有着某种紧密的联系。union如前面所说是两个数据集的并集,intersect是两个结果集的交集,except则是两个结果集的差集。以上面的表为例,如果使用intersect结果集将为空,因为这两个集合根本就没有相同的数据集。使用except则返回的是第一个select返回的结果。使用union和except的结果如下所示。
select studentId,studentName from student
union
select teacherId,teacherName from teacher
order by studentId,studentName select studentId,studentName from student
except
select teacherId,teacherName from teacher
order by studentId,studentName
2.从select into到临时表
在实际应用中开发者经常会需要去创建一个临时的表存储数据,select into正是扮演着这样的角色。使用select into要注意创建的表名必须是唯一的,当我们使用select into创建一个临时的表时会在当前数据库中建立一张新表。注意这张表已存在于当前数据库中,这样的话开发者还需要手动drop,如果忘记删除将会导致数据库中的表越来越多,而且很有可能重名。为了改进这一缺点,SQL利用了和C#里的垃圾回收一样的思想,就是由系统来删除临时表,当然我们也可以手动删除。我们只需要在设置临时表名前加上#或##,一个#表示本地临时表,两个#表示全局临时表。对于本地临时表,最重要的特点是创建后只对当前这次会话状态有效,一次会话状态指的是客户端与数据库引擎的连接,这里客户端指登入者在数据库上进行操作的一端,最终执行是需要连接数据库引擎来完成的。比如select * into #table1 from student where studentId<4,执行后在系统数据库中的tempdb可看到创建的临时表。并且它的名字很有意思,sql可能是为了防止重名吧,它还为我们创建的本地临时表又添加了后缀名。当我关掉选项卡后,这个临时表也就从tempdb中消失了,也就是说本地临时表它属于创建它的当前用户,且只在当前会话状态下可使用。全局临时表创建后,所有用户都可见,当创建者介绍这次会话时比如我关掉选项卡,会发现全局临时表和本地临时表一起被删了。那是不是说全局临时表和本地临时表一样,也是当前会话状态结束就被系统删除呢?答案是否定的,这里和GC很像,如果没有其他任务引用这个全局临时表,那它就被删除。但要注意创建着会话状态存在时,其他任务可引用它创建的全局临时表,创建着会话状态存结束时,其他任务将不能再引用这个全局临时表,但是此时如果已有任务引用这个全局临时表不会删除,直至没有任务引用它。另外临时表并不是只有select into可以创建,我们也可以直接使用create table创建。
创建临时表和非临时表相比,有哪些优点和缺点呢?首先我们最关注的肯定是性能,临时表有一个特点那就是对它的操作不会记录日志文件,而非临时表则会进行记录,因此临时表性能比非临时表更快。在临时表中,由于本地临时表只对当前用户的当前会话状态有效,那么完全不需要对本地临时表进行加锁,所以从这一点来说本地临时表比全局临时表更快。显然这些性能的提高同时伴随着某些功能将无法使用,有利也有弊。临时表与非临时表一样,也可以建立索引、约束,只是不能建立外键约束,对于临时表和非临时表我们都可以使用drop和truncate来删除表。