SQL Note

了解SQL

SQL(Structured Query Language),结构化查询语言。是一种数据库语言。
笔者看了《SQL必知必会》,总结了一部分SQL语言的用法。

先说下一些基本知识。
SQL是维护关系型数据库中数据的一种标准计算机语言,非关系型数据库没有SQL的说法。
主键和外键:
主键:
1)主键不是必须的,但是每个表最好都有主键。
2)任意两行的主键值不同,主键值不能为null。主键唯一标示每一行
3)主键不能修改或更新。
4)主键不能复用,一个已经被使用过的主键,即使这一行被删除,这个主键也不能被使用。
5) 每个表的主键只能有一个。主键可以是一列,也可以是一组列(此时,使用主键标识时,需要将一组列都使用上)。

外键:
1)表的外键上是另一个表的主键(或者说表的外键的值必须在另一个表的主键中)。
2)外键用于和其他表建立联系。
3)保证引用完整性。建立外键后,不允许删除尚有关联的行,防止意外删除数据,但是有的DSMS也允许联级删除。
4) 一个表可以有多个外键。

SQL中的关键字:
和别的语言一样,SQL语言有保留的关键字,这些关键字不能作为表或列的名字。

SQL语句的格式:
1)多条SQL语句之间用;分割,单条SQL语句之后可以不用;分隔,但是最好都加上。
2)SQL语句不区分大小写。(个人比较喜欢小写,但是专业人士好像都喜欢大写SQL关键字,表名和列名小写)
3)SQL语句中的所有空格和换行都被忽略。将SQL语句写成多行且有缩进的形式会比较好读。

SQL语句的注释:
1)行内注释:--#,后一种比较少用。
2)行间注释:/**/的组合。

检索数据

SQL语言中很大一部分是查询语句,差不多是用的最多的。

select

检索单个列:

select column1 from table1 ; 

检索多个列:
列与列之间用逗号分隔,最后一个列后面不用逗号。

select column1, column2, column3 from table1 ; 

检索所有列:
使用通配符 * ,可以用于查找未知列。

select * from table1 ; 

检索不同的值:
select默认检索处所有的值,如果想要去除重复的值,可以使用distinct。
distinct作用于后面所有的列,比如 distinct column1, column2,必须column1和column2的值都相同,才会认为相同,然后被剔除。

select distinct column1 from table1 ; 

限定检索条数:
select语句默认检索出所有的值,使用limit和offset可以指定返回特定的记录。
limit后面的是限定的条数(如果没有这么多条,也不会报错),offset是指定从哪开始(index从0开始)。

select column1 from table1 limit 1 offset 2 ; 

另一种格式:

select column1 from table1 limit 2, 1 ;     

单独的select语句只能用于简单的查找,与别的子句一起使用,功能会更强大。

order by

使用 order by子句对检索出来的记录进行排序。
要注意的是,即使没有排序,检索出来的记录也不是随机的,可能和当初存入数据库的先后有关,经过删除之类的操作,顺序可能会被改变。
order by子句应该在where子句之后。

单列排序:
order by 默认是升序排序,如果要降序排序,要指定desc。

select column1, column2 from table1 order by column1 desc ; 

多列排序:
当第一列的值相同时,根据第二列的值排序,当第二列的值相同时,根据第三列的值排序,以此类推。
desc 只对它前面的列起作用。

select column1, column2 from table1 order by column1, column2 desc ; 

where

select语句中使用where语句过滤条件,过滤条件也称搜索条件。 where子句一般在from子之后。

where子句的操作符:

等于     = 
不等于   !=, <> 
大于等于  >=, !<
小于等于  <=, !> 
小于     <
大于     > 
两值之间  between ... and ...  (是闭区间,包括开始值和结束值) 
为空值   is null 

where 子句可以单独使用,也可以组合使用。
where子句可以用 andor连接。(逻辑操作符连接)and 的 优先级比 or 高 。 还是建议使用括号。

select column1 from table1 where column1 is null and column2 = 'ahha' ; 

注意是一个where之后有多个条件。

可以使用in:

select column1 from table1 where column2 in (val1, val2, val3) ; 

in 的功能可以用 or实现,但是对于制定值的筛选,in比or更直观 。 而且 in 可以包括其他 select语句(子查询),更动态的建立where子句。

可以使用not:
not否定其后面的条件,not用于否定其后条件的关键字。

like

like子句用于对未知值的过滤。
like一般和通配符一起使用(通配符只能用于字符串匹配)
通配符:

%      任何字符出现任何次数(0次,1次,或多次)但不能匹配null
_      单个字符 
[]     指定字符集 
[^]    指定字符集的否定

不要过度使用通配符,会减慢速度。尽量不要把通配符放到模式搜索的开始出,会减慢速度。

创建计算字段

个人理解计算字段就是把数据库中的记录转化为应用程序所需要的格式。

拼接计算字段:

拼接字段          + 或 || 
去除左空格        ltrim() 
去除右空格        rtrim()
去除两边空格      trim() 

对于计算字段可是使用列别名:

select '(' + column1 + ')' as othername from table1 ; 

执行计算字段

加              + 
减              -
乘              * 
初              /

同样也可以使用列别名

select column1*column2 as othername from table1 ; 

函数

只有少数的几个函数被主要的DBMS同等的支持。所以使用函数的时候,最好去相应的DBMS的文档查询。

文本处理函数:

返回左边的字符                 left()
返回右边的字符                 right()
返回字符串的长度               length()
转化为小写                    lower()
转化为大写                    upper() 
去除左边空格                  ltrim()
去除右边空格                  rtrim()
去除两边的空格                trim()
返回字符串的soundex值         soundex()       soundex是字符串的语音表述的字母数字算法

数据处理函数(在各个DBMS中最为一致)

绝对值                       abs()
余弦值                       cos()
正弦值                       sin()
指数值                       exp()
圆周率                       pi()
平方根                       sqrt() 
正切                         tan() 

聚集函数

聚集函数用于汇总表中数据,有些数据不需要从表中检索出来,只用于汇总。
聚集函数的执行效率比在客户端应用程序计算的快得多。

某列平均值                     avg() 
某列行数                     count()
某列最大值                    max() 
某列最小值                    min() 
某列总和                     sum() 

avg()忽略为null的行。
count(* ) 包括为null的值(不指定列), count(column)忽略为null的值(指定列)。
max()和min()忽略为null的值,对于文本数据,max()返回最后的行,min()返回最前的行。
sum() 忽略为null的行。

执行所有计算                 all (默认就是all)
只包含不同的值               distinct (后面必须使用列名,不能是计算或表达式)

如:

select avg(distinct column1) as othername from table1 ; 

分组数据

分组数据用于汇总子集数据,涉及 group by和 having。

用一个例子解释分组

select v_id, count(*) 
from table1 
group by v_id 

上面这个例子中 count(* ) 不是对所有的行进行计数,而是先进行对v_id进行分组,count 计算的是 每个v_id相同的子集中的行数。这就是分组的作用。

group by使用的要点:

1)group by子句可以包括多个列,嵌套多个分组
2)如果group by嵌套了多个列,是对所有的列一起分组,不能从中拉取某一列的分组。 比如 group by x, y 就是 所有x和y一样都分在一组里面。
3)group by的每个参数,都必须是检索列或者有效的表达式。 如果在select语句中有表达式,必须在group by语句中有相同的表达式,不能是别名。 如:select a+b as othername, from table1 group by a+b
4)对于分组中值为null的行,会把这些行分为一组。
5)group by语句必须在where语句之后,order by语句之前。
6)group by给出的顺序可能不是分组的顺序,不能用group by代替order by。

having和where很相似,如果having不指定group by,大多数的DBMS会把它当作where。 但是一般having都是和指定的group by一起用的。
having 用于过滤分组,where用于过滤行。
where在数据分组前进行过滤,where排除的行不包括在分组中,having在数据分组之后进行过滤。
having支持where的操作符。

最后 select语句及其顺序

子句                        说明                         是否必需
select                     返回列或表达式                 是
from                       从中检索数据的表               仅从表中获取数据时必须
where                      行级过滤                      否
group by                   分组说明                      仅在分组聚集时使用
having                     组级过滤                      否
order by                   排序                          否

子查询

子查询是嵌套在查询中的查询。
子查询对嵌套的层数没有限制,但是最好不要太多层的嵌套。 子查询是从内到外的查询。
子查询只能查询单个列,返回单个列,不然会报错。

where子句后使用子查询,

select column1 
from table1 
where column1 in (  select column1 
                    from table2 
                    where table2.column1 = table1.column1 ) ; 

注意这里使用了完全限定列名, 格式为 表名.列名 因为 table1和table2中列名一样,如果不使用完全限定列名,会混淆。

子查询还可以作为计算字段使用:

select column1, 
       ( select count(*) 
            from table2
            where table2.column2 = table1.column1 )
from table1 ; 

联结表

如果要查询的数据在多个表中,就要联结表。
联结表时,要指定联结条件,如:

select column1, column2, column3 
from table1, table2 
where table1.column1 = table2.column3

其中column1,column2在table1中,column3在table2中。
where后面的就是联结条件,如果不指定联结条件,默认的联结是笛卡尔积,所以一般都是要指定联结条件的,因为笛卡尔积不是我们想要的。

联结可以和聚集函数一起使用,获取一个或多个表中的汇总数据。
最应当注意的是,联结应该提供联结条件。

等值联结/内联结

基于两个表之间相等测试叫等值联结,也叫内联结。 也可以写成以下格式:

select column1, column2, column3 
from table1 inner join table2 on table1.column1 = table2. column3 ; 

on后面的就是联结条件。

也可以进行多个表的联结,from后面列出所有要联结的表,然后定义表与表之间的联结条件。
注意不要联结太多表,会消耗资源,降低效率。

自联结

自联结就是在同一个表中的联结。

自联结可以使用表别名。表别名的形式:

from table1 as othertablename ....

然后子句中就可以使用这个表的别名。 表的别名可以缩短子句的长度,可是使同一个select语句中多次使用同一个表(对同一个表起不同的别名)。

select c1.column1, c1.column2, c1.column3 
from table1 as c1, table1 as c2
where c1.column1 = c2.column1 
and c2.column2 = val1 ; 

自联结可以代替从相同表中查询的子查询语句,而且效率比子查询语句高。

自然联结

联结的要求就是至少有一列会出现在不止一个表中(不然没有联结条件,很有可能就是笛卡尔积),所以就有可以在查询的结果中出现同一个列出现多次的情况。
自然联结就是排除出现多次的列,每个列最多只出现一次。

DBMS不会帮助实现自然联结,需要手动实现(其实就是select的时候注意一下不要返回相同的列就行了)。
自联结一般都是自然联结(除非你手动返回一样的列)。

外联结

外联结包含了那些在相关表中没有关联行的行(我觉得就是把为null的行也联结进去了)。
外联结包括了 左外联结(left outer join) 和 右外联结 ( right outer join)
左外联结就是选择左边表中所有的行(包括为null的行),右外联结就是包括了右边表中所有的行(包括为null的行)。左外联结和右外联结可以互相转换,改一下左右表的位置就行了。
还有一种是全外联结(full outer join)但是被支持的较少。

select column1, column2, column3 
from table1 left outer join table2 on table1.column1 = table2. column3 ; 

组合查询

组合查询允许执行多个查询(多个select语句)并将结果作为一个查询结果返回。( 其实就是并操作)
主要有两种情况使用组合查询:

1)在一个查询中从不同的表中查询
2)对一个表执行多个查询,按一个查询返回数据。(与具有多个用or连接的where子句的单个select查询的比较?书上122页感觉有点讲错了)

组合查询的格式:对每条select语句用union连接。

select column1, column2, column3
from table1 
where 过滤条件1。
union  
select column1, column2, column3
from table2 
where 过滤条件2 ; 

组合查询的规则:
1) 组合查询必须由两个或两个以上的select语句组成,用union连接
2) union中的每个查询必须包括相同的列,表达式或聚集函数。 (但次序可以不同)
3) 列数据的类型要可以兼容,数据类型可以不同,但是必须可以被转换。

union默认去除相同的行,如果要保留相同的行,可以使用union all。

如果要对结果进行排序的话,order by放到最后,且只有一个order by,对查询的所有结果进行排序。

插入数据

使用insert插入数据。

1) 插入完整行(必须给每个列提供一个值)

select into table1 
values(
    val1,
    val2,
    val3
    .
    .
    ) ; 

这种不指定列名,虽然比较方便,但是不安全建议写成这种(指定列名):
键值对一一对应。

select into table1)(
            column1,
            column2,
            column3,
            .
            .
            ) ; 
values(
    val1,
    val2,
    val3,
    .
    .
    ) ; 

2)插入部分行

使用上面指定列名的方式也可以用于插入部分行,不指定的行默认为空值或使用默认值。
也就是说不指定的行必须能是null或有默认值。

3)插入检索出的数据

可以将检索出的行插入,这就是insert select语句,有insert 和select 语句组成。
insert select的优点就是可以一条insert插入多行,一般的insert语句只能插入一行。

insert into table2(
            column1, 
            column2,
            column3,
            .
            .
            ) ; 
select 
    column1, 
    column2, 
    column3,
    .
    .
from table1 ; 

上面这个例子就是吧table1中的数据检索出来,插入早table2中。注意,两个表的列名可以不相同,不匹配,插入是根据位置匹配的。 select语句中可以用where进行过滤。

4) 复制表

从一个表中把所有数据复制到到一个新表,使用select into

select * 
into newtable
from oldtable ; 

这个语句可以自动创建新表,如果newtable已经存在,也会被覆盖。
任何select选项的和子句都可以使用比如where 和order by,也可以选入特定的列插入而不使用通配符。
也可以利用联结从多个表中选出数据。但是最后,只能插入到一个表中。

更新数据

使用update更新表中数据。

1)更新所有的行

update table1
set column1 = newvalue ;

没有where语句进行过滤,update会把某列的所有数据都更新,这是很危险的,要很小心。
所以其实可以用update来删除特定的列(把整列更新为null)。

2)更新特定的行

update table1
set column1 = newvalue 
where 过滤条件 ; 

使用where过滤出特定的行,或者也可以使用子查询。

删除数据

使用delete删除表中数据。

1) 删除所有行

delete from table1 ;

上述语句会把table1中的所有行删除(这是很危险的),但是table1还在。delete不需要列名或通配符,delete删除的是行,如果要删除列,可以用update。

删除时要注意引用完整性(外键约束)。

2) 删除特定行

delete from table1
where 过滤条件1 ;

用where过滤出特定的行,或者也可以使用子查询。