SQL(Structured Query Language)是一种专门用来与数据库沟通的语言
主键:一列或一组列,其值能够唯一标识表中的每一行表中的任何列都可以作为主键,但必须满足以下条件:
任意两行都不具有相同的主键
每一行都必须具有一个主键值(主键列不允许为NULL值)
主键列中的值不允许修改或更新
主键值不能重用(如果某行从表中删除,它的主键不能赋值给以后的新行)
利用SELECT语句来检索表数据,至少需要给出两条信息—想要找什么,以及从什么地方找,下面是查找[QXYJ].[dbo].[JD_REGION]表中具有NAME,CODE属性的数据,查找结果只返回PROD_ID, PROD_NAME, PROD_PRICE 三列数据。使用*可以检索所有列。
SELECT PROD_ID, PROD_NAME, PROD_PRICE
FROM [MASTER].[DBO].[Products]
利用DISTINCT关键字检索不同的值,加上DISTINCT关键字后将显示唯一值,相同的值只出现一次
SELECT distinct VEND_ID
FROM [MASTER].[DBO].[Products]
使用注释,–和#以及/**/都可以用于SQL语句的注释
使用SELECT语句的ORDER BY子句,根据需要排序检索出的数据
使用ORDER BY子句可以对输出的检索数据进行排序,其中ORDER BY 子句必须放在SELECT语句的最后面,否则将报错
SELECT PROD_NAME
FROM [MASTER].[DBO].[PRODUCTS]
ORDER BY PROD_NAME
当需要按多个列进行排序时,列名之间用,来隔开,下面是对列的结果先进行价格排序,然后按照名称排序,价格排序的优先级要大于名称排序,还可以根据列的相对位置来排序,下列语句中注释的语句是一样的排序效果
SELECT PROD_ID,PROD_price,prod_name
from [master].[dbo].[products]
order by prod_price,prod_name
# order by 2,3
通过DESC关键字(DESCENDING)来指定排序方向,排序默认是升序排序(从A到Z),可以使用DESC来降序排序,但是遇到多列排序的状况,DESC只对它前面的列名有效(只有一个有效),若需要对多个列名降序排序,需要在每个列名后添加DESC,如下列语句,DESC只对prod_price有效 (ASC是升序排序(ASCENDING))
select prod_id,prod_price,prod_name
from [master].[dbo].[products]
order by prod_price DESC, prod_name
使用SELECT语句的WHERE子句来指定搜索条件,WHERE语句写在FROM子句后面,若碰到ORDER BY子句,ORDER BY子句是放在所有子句的最后面,如下面的搜索语句:
select prod_name,prod_price
from [master].[dbo].[products]
where prod_price=3.49
操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
!< | 不小于 |
> | 大于 |
>= | 大于等于 |
!> | 不大于 |
BETWEEN | 在指定的两个值之间 |
IS NULL | 为NULL值 |
select prod_name,prod_price
from [master].[dbo].[Products]
where prod_price between 5 and 10
范围值检查:检查5到10之间的值,使用BETWEEN关键字
使用组合WHERE子句来建立更强,更高级的搜索条件,并学习如何使用NOT和IN操作符
SELECT PROD_ID,PROD_PRICE,PROD_NAME
FROM [MASTER].[DBO].[PRODUCTS]
WHERE VEND_ID='DLL01' AND PROD_PRICE<=4
上面是检索由供应商DLL01制造且价格小于等于4美元的所有产品的名称和价格
AND关键字是用来指示检索满足所有给定条件的行;OR关键字用来表示检索匹配任一给定条件的行。但是AND的优先级高于OR,当两个关键字组合使用时,应该考虑到优先级的问题。IN用来指定要匹配值的清单的关键字,功能与OR相当;NOT用来在WHERE子句中否定其后条件的关键字
使用IN操作符:IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配,IN取一组由逗号分隔,括在圆括号中的合法值。
select VEND_ID,prod_name,prod_price
from [master].[dbo].[products]
where vend_id in ('DLL01','BRS01')
#where vend_id='DLL01' or vend_id='BRS01' 一样的效果
ORDER BY PROD_NAME
上面的检索语句是检索由供应商DLL01和BRS01制造的所有产品,IN操作符完成了与OR关键字相同的功能,因此上述代码中的or的结果与IN的结果是一样的
NOT操作符:NOT只有一个功能,就是否定其后跟的任何条件,需要与其他操作符一起使用,NOT操作符与<>操作符的功能相似
select vend_id,prod_name
from [master].[dbo].[products]
where not vend_id='DLL01'
ORDER BY vend_id
列出除DLL01之外的所有供应商制造的产品
使用通配符(%、_、[]),以及 like操作符进行通配搜索
通配符搜索只能用于文本字段(字符串),非文本数据类型字段不能使用通配符搜索
like操作符
百分号(%)通配符是最常使用的,在搜索中,%可以出现任意次数
找出所有以词Fish起头的产品,搜索语句如下,检索语句将检索以Fish开头的词,%表示可以接受Fish之后的任意字符,不管有多少字符
select prod_id,prod_name
from [master].[dbo].[products]
where prod_name like 'Fish%'
通配符可以在任意位置使用,并且可以使用多个通配符,下面的搜索语句是匹配中间的特定字符的数据
select prod_id,prod_name
from [master].[dbo].[products]
where prod_name like '%bean bag%'
下划线(_)通配符:下划线的用途与%一样,但是它只能匹配单个字符
select prod_id,prod_name
from [master].[dbo].[products]
where prod_name like '__ inch teddy bear'
#上面是两个下划线
上面的搜索语句中有两个下划线,因此匹配的字符都是两位数的,数字在字符串中每个数字都是代表了一个字符
方括号([])通配符:用来指定一个字符集,它必须匹配指定位置的一个字符
找出所有名字以J或M起头的联系人,[JM]匹配了方括号中的任意一个字符,并且它也只能匹配单个字符,注释的语句是用来匹配除J和M以外的任意字符起头的任意联系人名
select cust_contact
from [master].[dbo].[Customers]
where cust_contact like '[JM]%'
# where cust_contact like '[^JM]%'
# where NOT cust_contact like '[JM]%'
order by cust_contact
当我们需要的数据并不是全部在一个表中时,需要从数据库中检索出转换、计算或格式化的数据。
计算字段并不实际存在于数据表中,而是在运行时在select 语句中创建的
拼接字段:使用+将两个值连接到一起,连接后的字符在表中并不存在,其中的RTRIM函数是去除每个值的右边的空格,LTRIM是去掉字符串左边的空格,TRIM去除字符串左右两边的空格
select RTRIM(vend_name) + '(' + RTRIM(vend_country) +')'
from [master].[dbo].[Vendors]
order by vend_name
将vendors表中的两个列 vend_name和vend_country连接起来
执行算术计算:对检索的数据进行算术计算
select order_num, prod_id, quantity, item_price, quantity*item_price AS expanded_price
from [master].[dbo].[orderItems]
where order_num=20008
函数 | 语法 |
---|---|
提取字符串的组成部分 | SQL Server和MySQL使用SUBSTRING();Oracle,PostgreSQL和SQLite使用SUBSTR() |
数据类型转换 | MySQL和SQL Server使用CONVERT();PostgreSQL使用CAST() |
获取当前日期 | SQL Server使用GETDATE();MySQL使用CURDATE();SQLite使用DATE() |
函数 | 说明 |
---|---|
LEFT() | 返回字符串左边的字符 |
LENGTH()、LEN() | 返回字符串的长度 |
LOWER() | 将字符串转换为小写 |
LTRIM() | 去掉字符串左边的空格 |
RIGHT() | 返回字符串右边的字符 |
RTRIM() | 去掉字符串右边的空格 |
SOUNDEX() | 返回字符串的SOUNDEX值 |
UPPER() | 将字符串转换为大写 |
SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法
函数 | 说明 |
---|---|
ABS() | 返回一个数的绝对值 |
COS() | 返回一个角度的余弦 |
EXP() | 返回一个数的指数值 |
PI() | 返回圆周率 |
SIN() | 返回一个角度的正弦 |
SQRT() | 返回一个数的平方根 |
TAN() | 返回一个角度的正切 |
函数 | 说明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
AVG()函数
AVG()函数是通过对表中行数进行计数并计算某列值之和,求得该列的平均值。AVG()函数可用来返回所有列的平均值,也可以用来返回特定列或行的平均值
select AVG(prod_price) as avg_price
from products
#where vend_id='DLL01'
返回的值是所有产品的平均价格
若加上where筛选语句,就是返回的是DLL01产品的均值,若想获得多个列的平均值,就需要多次使用AVG()函数
COUNT()函数
COUNT()函数用于确定表中行的数目或符合特定条件的行的数目
COUNT()函数有两种使用方式
使用COUNT(*)对表中行的数目进行计数,不管列中包含的是空值(NULL)还是非空值
select COUNT(*) AS num_cust
from customers
返回的是Customers表中顾客的总数,在这种用法中,不管行中各列都有什么值,都会进行计数
select COUNT(cust_email) as num_cust
from Customers
该使用方式只对具有电子邮件地址的客户计数
MAX()函数
返回指定列的最大值,该函数要求指定列名
select MAX(prod_price) AS max_price
from products
返回的是products表中最贵物品的价格
注意:若是对非数值数据使用MAX(),MAX将返回该排序后的最后一行
MIN()函数
返回指定列的最小值,该函数要求指定列名
select MIN(prod_price) AS min_price
from products
返回的是products表中最便宜物品的价格
SUM()函数
用来返回指定列值的总和
select SUM(quantity) as items_ordered
from OrderItems
返回订单表中所有物品数量之和,可以添加where子句来查询某个物品订单的数量
select SUM(item_price*quantity) as total_price
from orderItems
where order_num=20005
使用SUM()函数来合计计算值,返回order_num=20005的物品的总的订单金额
聚集不同的值,指定DISTINCT 参数
select AVG(DISTINCT prod_price) as avg_price
from products
where vend_id='DLL01'
排除相同值之后的平均价格,DISTINCT不能用于COUNT(*),只能用于COUNT(column)
组合聚集函数
select COUNT(*) as num_items,
MIN(prod_price) as price_min,
MAX(prod_price) as price_max,
AVG(prod_price) as price_avg
from products
执行多个聚集函数,返回不同的结果
数据分组
select COUNT(*) as num_prods
from products
where vend_id='DLL01'
上面的只是显示一个供应商提供的产品数量,若需要返回每个供应商提供的产品数目,则需要用到分组。使用分组可以将数据分为多个逻辑组 ,对每个组进行聚集计算
创建分组
select vend_id, COUNT(*) as num_prods
from products
group by vend_id
group by 子句指示DBMS按vend_id排序并分组数据,这就会对每个vend_id而不是整个表计算num_prods一次。
使用了group by,就不必指定要计算和估值的每个组了,系统会自动完成;group by 子句指示DBMS分组数据,然后对每个组而不是整个结果集进行聚集
使用group by 子句的一些重要规定:
- group by 子句可以包含任意数目的列,因而可以对分组进行嵌套,更细致的进行数据分组
- 在建立分组时,指定的所有列都一起计算(所以不能从个别的列取回数据)
- group by子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在select中使用表达式,则必须在group by子句中指定相同的表达式,不能使用别名。
- 大多数SQL不允许GROUP BY列带有长度可变的数据类型(如文本或注释字段)
- 除聚集计算语句外,SELECT语句中的每一列都必须在group by子句中给出
- 如果分组列中包含具有NULL值的行,则NULL将作为一个分组返回,如果列中有多行NULL值,它们将分为一组
- group by子句必须出现在where子句之后,order by子句之前
过滤分组
除了能用group by子句分组数据外,SQL还允许过滤分组,规定包括哪些分组,排除哪些分组。过滤分组使用的是HAVING子句而不是where子句。HAVING与where十分相似,唯一的差别是where是过滤行,而HAVING是过滤分组,并且HAVING支持WHERE所有的操作符
select cust_id,COUNT(*) as orders
from orders
group by cust_id
having COUNT(*)>=2
上述代码过滤的是COUNT(*)>=2的那些分组
子查询:嵌套在其他查询中的查询
在数据库中查找订购物品RGAN01的所有顾客
- 检索包含物品RGAN01的所有订单的编号
- 检索具有前一步骤列出的订单编号的所有顾客的ID
- 检索前一步骤返回的所有顾客ID的顾客信息
select order_num
from orderItems
where prod_id='RGAN01'
将此处查询得到的结果用于下一步骤
select cust_id
from orders
where order_num in (20007,20008)
同样将此处查询的结果用于下一步骤,这里使用了IN操作符
select cust_name,cust_contact
from customers
where cust_id in('1000000004','1000000005')
得到最终的结果
使用子查询将三个查询语句结合在一起
select cust_name,cust_contact
from customers
where cust_id in(select cust_id
from orders
where order_num in (select order_num
from orderItems
where prod_id='RGAN01'))
将得到一样的结果
子查询只能查询单列,检索多个列将报错
作为计算字段使用子查询
查询customers表中每个顾客的订单总数
- 从customers表中检索顾客列表
- 对于检索的每个顾客,统计其在orders表中的订单数目
select cust_name,cust_state,(select count(*)
from orders
where orders.cust_id=customers.cust_id) as orders
from customers
order by cust_name
该子查询会对检索出的每个顾客执行一次,子查询一共执行了5次,因此查询了5个顾客
关系表:将信息根据类别拆分成多个表,一个类别一个表,避免了数据的冗余,各表通过某些共同的值相互关联。
创建联结
select vend_name,prod_name,prod_price
from vendors,products
where vendors.vend_id=products.vend_id
/*这两个代码检索的结果是一样的*/
select vend_name,prod_name.prod_price
from vendors inner join products
on vendors.vend_id=products.vend_id
上述两个表的关系是以inner join指定的部分from子句,在使用这种语法时,联结条件用特定的on子句而不是用where子句,但传递的条件是与where子句是相同的。这种联结方式也被称为内联结
与之前的搜索语句不通的是:搜索的列中,vend_name在vendors表中,而 prod_name,prod_price在products表中,from的是两个表 ,而且这 两个表用where子句来联结
联结多个表
select prod_name,vend_name,prod_price,quantity
from orderitems,products,vendors
where products.vend_id=vendors.vend_id
and orderitems.prod_id=products.prod_id
and order_num=20007
这个例子是列出订单是20007的物品,这里定义了两个联结条件,最后的是过滤子句
上一节中的子查询可以用联结表来代替
select cust_name,cust_contact
from customers
where cust_id in (select cust_id
from orders
where order_num in (select order_num
from orderitems
where prod_id='RGAN01'))
select cust_name,cust_contact
from customers.orders,orderitems
where customers,cust_id=orders.cust_id
and orderitems.order_num=orders.order_num
and prod_id='RGAN01'
子查询与联结表查询得到的结果是一样的
列名的表示方法
select RTRIM(vend_name) + '('+RTRIM(vend_country)+')' as vend_title
from vendors
order by vend_name
表名的表示方法
select cust_name,cust_contact
from customers as C ,orders as O, orderitems as OI
where C.cust_id=O.cust_id
and OI.order_num=O.order.num
and prod_id='RGAN01'
外联结
许多联结将一个表中的行与另外一个表中的行进行关联,但在联结过程中还需要包含那些在相关表中没有关联的行,这种联结就称为外联结
select customers.cust_id,orders.order_num
from customers left outer join orders
on customers.cust_id=orders.cust_id
这里使用的是外联结,检索包括没有订单顾客在内的所有顾客,并使用customers表中的所有行
在使用OUTER JOIN关键字时,必须使用RIGHT或者LEFT关键字来指定所有行的表(RIGHT指出的是OUTER JOIN右边的表,LEFT指出的是OUTER JOIN左边的表)
内联结检索的是所有顾客及其订单
select customers.cust_id,orders.order_num
from customers inner join orders
on customers.cust_id=orders.cust_id
使用组合查询的两种情况:
- 在一个查询中从不同的表返回结构数据
- 对一个表执行多个查询,按一个查询返回数据
创建组合查询,使用UNION关键字
select cust_name,cust_contact,cust_email
from customers
where cust_state in ('IL','IN','MI')
UNION
/*UNION ALL*/ /*使用ALL的话将返回所有的行,重复的不会被过滤*/
select cust_name,cust_contact,cust_email
from customers
where cust_name='Fun4All'
select cust_name,cust_contact,cust_email
from customers
where cust_state in ('IL','IN','MI')
or cust_name='Fun4All'
上述两段代码检索的结果是一样的,顺序可能不同。对于一些复杂的过滤条件,使用UNION处理会更简单
使用UNION规则:
- UNION必须由两条或两条以上的select语句组成,语句之间用关键字UNION来分隔
- UNION中的每个查询必须包含相同的列、表达式或聚集函数(列名最好以相同的次序列出,不然会造成重复)
- 列数据类型必须兼容:类型不必完全相同,但是必须能够被转换
- 若想使用order by进行排序,则该语句必须放在最后一个select语句中,并且只允许出现一次order by子句
INSERT关键字用来将行插入到数据库表中,有几种插入方式:
- 插入完整的行
- 插入行的一部分
- 插入某些查询的结果
INSERT INTO Customers(cust_id,cust_name,cust_address,
cust_city,cust_state,cust_zip,
cust_country,cust_contact,cust_email)
values('1000000006','Tony Land','123 AAny Street',
'New York','NY','11111','USA',NULL,NULL)
插入的结果在最后一行,其中值需要与列名一一对应,列名不需要与原表的列名顺序一样,其中values中的值要完整,即使该值为NULL,但也可以省略
但是若原表在定义允许在INSERT操作中 省略某些列,则可以进行省略,但必须满足以下条件:
- 该列定义为允许NULL值(无值或空值)
- 在表定义中给出了默认值,若插入中没有值,则使用默认值
插入检索的数据:可以将select语句中检索的结果插入到表中。(INSERT SELECT)
INSERT INTO Customers(cust_id,cust_name,cust_address,
cust_city,cust_state,cust_zip,
cust_country,cust_contact,cust_email)
select cust_id,cust_name,cust_address,cust_city, cust_state,cust_zip,cust_country, cust_contact,cust_email
from CustNew
该例子是将CustNew中检索得到的结果插入到Customers中,在select中也可以使用where子句来过滤结果
从一个表复制到另一个表(SELECT INTO)
INSERT SELECT 与SELECT INTO的区别:前者插入数据,后者导出数据
select *
into custCopy
from customers
上述代码是创建一个custCopy的新表,并把Customers表的整体内容复制到新表中,这里使用的是*通配符,因此是将所有内容都复制,可以选择只复制部分列
更新数据:使用UPDATE来更新数据,更新可以选择更新表中的特定行还是更新表中的所有行
基本上的update语句由三部分组成:
- 要更行的表(使用update)
- 列名和它们的新值(set来设置)
- 确定要更新哪些行的过滤条件(where子句)
- 改变多行时只需要使用一个set
update customers
set cust_email='[email protected]',
cust_state='china'
where cust_id='1000000006'
更新数据时尽量不要省略where子句,否则将会更新整个表
删除数据
删除数据有两种方法:
- 将某列的值更新为NULL
- 使用DELETE来删除数据
- 从表中删除特定的行
- 从表中删除所有的行
/**删除cust_id为1000000005的cust_email值,更新为NULL也是删除*/
update customers
set cust_email=NULL
where cust_id='1000000005'
从customers表中删除一行
delete from customers
/*若没有where子句,则表中数据将全部删除*/
where cust_id='1000000006'
使用create table关键字来创建表,必须给出以下信息:
- 新表的名字,在关键字create table之后给出,新表名字不能与现有表名重复
- 表列的名字和定义,用逗号隔开
- 有的DBMS还要求指定表的位置
/*创建Products表*/
/*可以指定某列为NULL值,但是可以为NULL值的列不能作为主键*/
create table products(
prod_id char(10) NOT NULL,
vend_id char(10) NOT NULL,
prod_name char(254) NOT NULL,
prod_price DECIMAL(8,2) NOT NULL,
prod_desc varchar(10000) NULL,
)
create table orderItems(
order_num integer not null,
order_item integer not null,
/*可以指定默认值,如果插入行的该属性不赋值,将使用默认值*/
quantity integer not null default 1,
item_price decimal(8,2) not null,
)
使用ALTER TABLE关键字来更新表,必须要要给出下列信息:
- 在alter table之后必须要给出更改的表名,该表必须存在,否则将出错
- 列需要作出哪些更改
小心使用alter table,数据库表的更改不可撤销,一旦增加或删除,将不可撤销
/*给vendors表增加一个名为vend_phone的列,其数据类型为char*/
alter table vendors
add vend_phone char(20)
alter table vendors
/*删除vend_phone列*/
drop column vend_phone
删除表
使用drop table关键字来删除表,该删除语句一旦执行也无法撤销,将永久删除该表
drop table custcopy
约束
主键:是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,而且永不变动。
表中任意列只要满足以下条件,就可用于主键
- 任意两行的主键值都不同
- 每行都具有一个主键值(即列中不允许NULL值)
- 包含主键值的列从不修改或更新
- 主键值不能重用,若从表中删除某一行,其主键值不能分配 给新行
/*在创建表时就添加主键*/
create table vendors(
vend_id char(10) NOT NULL PRIMARY KEY,
vend_name char(50) not null,
vend_address char(50) null
)
/*使用constraint关键字来定义主键*/
alter table vendors
add constraint primary key (vend_id)
/*表定义时使用了references关键字 ,表示cust_id中的任何值都必须是customers表的cust_id中的值,这就添加了一个外键*/
create table orders(
order_num integer not null primary key,
order_date datetime not null,
cust_id char(10) not null references customers(cust_id)
)
/*使用constraint关键字来添加外键*/
alter table orders
add constraint foreign key (cust_id) references customers(cust_id)
唯一约束:用来保证一列(或一组列)中的数据是唯一的。
唯一约束与主键类似,但有以下区别:
- 表可以包含多个唯一约束,但只允许有一个主键
- 唯一约束列可以包含NULL值
- 唯一约束列可以修改或更新
- 唯一约束列的值可重复使用
- 与主键不一样的是,唯一约束不能用来定义外键
检查约束:用来保证一列(或一组列)中的数据满足一组指定的条件
检查约束通常用于以下几点:
- 检查最大值和最小值,如防止订单数为负数
- 指定范围,如特定的日期范围
- 只允许特定的值,如指定性别
索引:用来排序数据以加快搜索和排序操作的速度