主键:一列(或一组列),其值可以唯一标识表中每一行。
用SELECT语句检索数据
SELECT prod_name FROM Products;
表示从Products表中检索一个名为prod_name的列,结果会返回prod_name列。
SELECT prod_id,prod_name,prod_price FROM Products;
表示从Products表中选三个列,结果会返回三个列。
SELECT * FROM Products;
表示检索表中的所有列。
SELECT语句返回所有匹配的行,如果不希望有重复的值,可以使用DISTINCT
SELECT DISTINCT vend_id FROM Products;
从表中返回值唯一的vend_id列。
SELECT DISTINCT vend_id,prod_price FROM Products;
DISTINCT关键字会滤除两列都相同的数据,只有一列一样的话不会被滤除。
限制返回结果长度
在SQL Server和Access中,用TOP关键字
SELECT TOP 5 prod_name FROM Products;
表示返回前五行。
在DB2中,方法不同。
SELECT prod_name FROM Products FETCH FIRST 5 ROWS ONLY;
也是返回前五行。
如果是Oracle,
SELECT prod_name FROM Products WHERE ROWNUM<=5;
如果用MySQL、MariaDB、PostgreSQL或者SQLite,用LIMIT子句。
SELECT prod_name FROM Products LIMIT5;
还可以通过offset指定开始返回的是第几行。
SELECT prod_name FROM Products LIMIT 5 OFFSET 5;
表示从第六行开始返回五行(行数是从0开始的,所以5表示第六行)。
使用注释
"--"后面跟的就是注释。
还有一种注释方法是用两个/*将注释围起来。
排序数据
SELECT prod_name FROM Products ORDER BY prod_name;
从表中选出指定列后对它进行排序。
ORDER BY子句一定要放在查询语句的最后,否则会报错,另外,也可以用非检索列进行排序。
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price,prod_name
对多个列进行排序。如果prod_price中所有值都是唯一的,则不会按prod_name排序。
ORDER BY子句中,可以通过数字代表列名
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY 2,3;
表示根据检索的第二第三列进行排序。
可以指定排序方向,默认是升序,通过DESC关键字可以调整为降序。
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price DESC;
按prod_price降序进行排序。
SELECT prod_id,prod_price,prod_name FROM Products ORDER BY prod_price DESC,prod_name;
按照prod_price降序排序,再按prod_name进行升序排序。
所以如果想对多个列进行降序排序,必须对每一列指定DESC关键字。
使用WHERE子句进行过滤数据。
SELECT prod_name,prod_price FROM Products WHERE prod_price = 3.49;
表示选取指定列,并筛选出prod_price等于3.49的数据。
WHERE子句操作符列表
SELECT prod_name,prod_price FROM Products WHERE prod_price < 10;
选择prod_price小于10的数据。
SELECT vend_id,prod_name FROM Products WHERE vend_id <> 'DLL01';
从表中匹配指定列,并返回vend_id不等于DLL01的数据。
通过BETWEEN子句选择返回范围
SELECT prod_name,prod_price FROM Products WHERE prod_price BETWEEN 5 AND 10;
匹配指定列,并返回prod_price在5~10间的值。
NULL表示无值,与0,空字符串或空格不同。
SELECT prod_name FROM Products WHERE prod_price IS NULL;
返回prod_price为无的指定列。
组合WHERER子句
SELECT prod_id,prod_price,prod_name FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;
从表中选择指定的列并返回vend_id等于DLL01且prod_price小于等于4的数据。
SELECT pord_name,prod_price FROM Products WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';
从表中选择指定的列并返回vend_id等于DLL01或BRS01的数据。
当查询语句中同时有AND和OR语句时,有优先顺序的区别。
SELECT prod_name,prod_price FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' AND prod_price >= 10;
表示从表中选择指定列,并且满足vend_id为DLL01或(vend_id为BRS01并且prod_price大于等于10)的数据。
优先处理AND操作符。
SELECT prod_name, prod_price FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >= 10;
用括号将OR语句包起来,则会优先处理OR语句。
IN操作符用来指定条件范围。
SELECT prod_name.prod_price FROM Products
WHERE vend_id IN ('DLL01','BRS01') ORDER BY prod_name;
从表中选择指定的列,并返回vend_id等于DLL01或BRS01的数据,还要对prod_name进行排序。
IN操作符完成了与OR相同的功能,比一组OR操作符执行的更快,另外IN里面可以包含其他SELECT语句。
NOT操作符,用来否定后面跟的条件。
SELECT prod_name FROM Products WHERE NOT vend_id = 'DLL01' ORDER BY prod_name;
从表中选择指定列,并选择vend_id不等于DLL01的数据,并根据prod_name进行排序。这个例子也可以用<>操作符完成。
通配符,用来匹配值的一部分的特殊字符。
%表示任何字符出现任意次数。
LIKE操作符
SELECT prod_id,prod_name FROM Products WHERE prod_name LIKE 'Fish%';
从表中选择指定列,返回prod_name列中字符串开头为Fish的数。
SELECT prod_id, prod_name FROM Products WHERE prod_name LIKE '%bean bag%';
'%bean bag%'表示匹配任何位置上包含文本bean bag的值,不论它之前或之后出现什么字符。
SELECT prod_name FROM Products WHERE prod_name LIKE 'F%y';
找出以F开头,以y结尾的字符串。
另一个通配符"_",用途与%一样,但它只匹配单个字符。
SELECT prod_id,prod_name FROM Products WHERE prod_name LIKE '__ inch teddy bear';
匹配前面两个为任意字符,后面为' inch teddy bear'的字符串。
方括号[]通配符。(只有Access和SQL Server支持集合)
举个例子
SELECT cust_contact FROM Customers WHERE cust_contact LIKE '[JM]%' ORDER BY cust_contact;
匹配开头为J或者M的字符串。
SELECT cust_contact FROM Customers WHERE cust_contact LIKE '[^JM]%' ORDER BY cust_contact;
匹配非J和非M起头的字符串。
通配符搜索要耗费更长的时间,所以不要过度使用,确实需要用到时,尽量不要在搜索模式的开始处使用。
创建计算字段
拼接,将值联接到一起构成单个值。
Access 和 SQL Server 使用+号。DB2、Oracle、PostgreSQL、SQLite 和Open Office Base 使用||。
SELECT vend_name + '(' + vend_country + ')'
FROM Vendors ORDER BY vend_name;
将vend_name列和vend_country列合成一列。
使用'||'语法:
SELECT vend_name || '(' || vend_country || ')'
FROM Vendors ORDER BY vend_name;
MySQL 或 MariaDB 时使用concat语句:
SELECT Concat(vend_name,'(',vend_country,')')
FROM Vendors ORDER BY vend_name;
返回的结果中有很多空格,可以通过RTRIM()函数去除。
SELECT RTRIM(vend_name) + '(' + RTRIM(vend_country) + ')'
FROM Vendors ORDER BY vend_name;
RTRIM()表示去掉值右边的所有空格。
使用别名
像上面那个由两个列拼接出来的列没有列名,可以新命名一个。
SELECT RTRIM(vend_name) + '(' + RTRIM(vend_country) + ')' AS vend_title
FROM Vendors ORDER BY vend_name;
输出的和并列就有了列名。
对检索结果进行算术计算
SELECT prod_id,quantity,item_price,quantity*item_price AS expanded_price
FROM OrderItems WHERE order_num = 20008;
返回结果中不但有检索列,还有算术计算建立的新列。
使用函数
SELECT vend_name,UPPER(vend_name) AS vend_name_upcase
FROM Vendors ORDER BY vend_name;
从表中选择vend_name列,复制一个同样的列并通过UPPER函数将这个列变成大写。
常用的文本处理函数
日期和时间处理函数
SELECT order_num FROM Orders WHERE DATEPART(yy,order_date) = 2012;
从表中检索日期为2012年的order_num列。(SQL Server 中)
Access版本:
SELECT order_num FROM Orders WHERE DATEPART('yyyy',order_date) = 2012;
MySQL 和 MariaDB 具有各种日期处理函数,但没有 DATEPART(),可以使用名为YEAR()的函数从日期中提取年份
SELECT order_num FROM Orders WHERE YEAR(order_date) = 2012;
数值处理函数
汇总数据
聚集函数
SELECT AVG(prod_price) AS avg_price FROM Products;
选中表中的prod_price列并求平均值,将结果命名为avg_price列。
SELECT AVG(prod_price) AS avg_price FROM Products WHERE vend_id = 'DLL01';
将指定列中vend_id等于DLL01的列求平均值。
SELECT COUNT(*) AS num_cust FROM Customers;
计算表中有多少行。COUNT()函数用于计算指定列有多少行(NULL不计入)
SELECT COUNT(cust_email) AS num_cust FROM Customers;
计算指定列有多少行,NULL会被忽略。
和上面的总行数为5比较可以得出,有两行cust_email是无值的。
MAX()函数用于返回指定列中的最大值。
SELECT MAX(prod_price) AS max_price FROM Products;
如果指定列是非数值列,比如文本列,MAX()会返回文本列升序排序后的最后一列。
MIN()函数和MAX()函数相反。
SUM()函数返回指定列的和。
SELECT SUM(quantity) AS items_ordered FROM OrderItems WHERE order_num = 20005;
返回order_num为20005的quantity列的和。
也可以对算术计算所得列进行求和。
SELECT SUM(item_price*quantity) AS total_price
FROM OrderItems WHERE order_num = 20005;
默认情况下,聚集函数会计算指定列的除NULL外的所有值,如果一列中有很多重复值不希望被计算进去,需要使用DISTINCT参数,默认是ALL参数。
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products WHERE vend_id = 'DLL01';
表示计算prod_price列的非重复值的平均值,同时vend_id等于DLL01。
和之前全部数据计算的平均值相比较高,因为该列有很多重复的低价。
DISTINCE不能用于COUNT(*),DISTINCT必须使用列名,不能用于计算或表达式。
组合聚集函数
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;
返回表中有多少非NULL行,计算prod_price的最大值,最小值和平均值。
数据分组
创建分组
SELECT vend_id,COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
根据vend_id分组,并计算各分组的非NULL个数。
用HAVING来过滤分组,不能用WHERE
SELECT cust_id,COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
对cust_id进行分组,并计算每个分组的行数,返回其中行数大于等于2的结果。
SELECT vend_id,COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;
选择prod_price大于等于4的vend_id并分组,然后统计每个分组的行数,最后返回行数大于等于2的结果。
SELECT order_num,COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items,order_num;
表示将order_num列分组,并统计每组的行数,返回其中行数大于3的数据,并对items和order_num进行排序。
使用子查询
从订单表中查找商品名为RGAN01的订单编号。
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';
根据得到的订单编号,在Orders表中查询下订单的客户ID
SELECT cust_id
FROM Orders
WHERE order_num IN(20007,20008);
结合这两个查询,可以查找购买某商品的用户有哪些。
如果使用子查询,可以将上面两个查询结合起来。
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
得到订购商品RGAN01商品的顾客ID后,根据ID到顾客列表检索顾客信息。
SELECT cust_name,cust_contact
FROM Customers
WHERE cust_id IN('1000000004,'1000000005')
可以把WHERE子句转为子查询
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语句只能查询单个列。
假设需要获取每个顾客的订单总数。首先从顾客列表获取每个顾客的顾客ID,再到Orders表中根据顾客ID统计每个顾客的订单进行计数。
SELECT cust_name,
cust_state,
(SELECT COUNT(*) FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;
联接表
SELECT vend_name,prod_name,prod_price
FROM Vendors,Products
WHERE Vendors.vend_id = Products.vend_id;
从供应商表和产品表取指定的三列,前提是两个表的供应商id相同。
如果没有WHERE子句,第一个表中的每一行将会与第二个表中的每一行配对。
內联接inner join
SELECT vend_name,prod_name,prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
结果和上面的使用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的产品名,供应商名,产品价格和数量。
上面有个例子是要返回订购产品RGAN01的顾客列表,可以通过联接重写一个看起来简单的。
SELECT cust_name,cust_contact
FROM Customers,Orders,OrderItems
WHERE OrderItems.order_num=Orders.order_num
AND Orders.cust_id = Customers.cust_id
AND prod_id = 'RGAN01'
创建高级联接
使用表别名
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';
别名不但可以缩短SQL语句,还允许在一条SELECT语句中多次使用相同的表。
自联接,一次查询中多次使用相同的表。
例子,在Customers表中找出所有与某顾客同公司的顾客。
使用子查询的话做法是先从表中找出该顾客所在的公司名,再在表中找出所有根据公司名找出所有人。
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
FROM Customers
WHERE cust_contact = 'Jim Jones');
因为操作都是在同一个表中进行的,所以还有另一种做法是用自联接和表别名。
SELECT a.cust_id,a.cust_name,a.cust_contact
FROM Customers AS a,Customers AS b
WEHERE a.cust_name = b.cust_name
AND a.cust_contact = 'Jim Jones';
自然联接,有时候,有些列可能出现在多个表中,选择数据时要注意在列名前加上表名,如果直接返回,可能会导致相同的列多次出现。
外联接
举个例子
SELECT Customers.cust_id,Orders.order_num
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
意思是通过左联接联接Customers表和Orders表,返回俩表cust_id相同的数据,因为是左联接,所以即使没有匹配到,Customers表的数据也会全部出现。
如果是RIGHT OUTER JOIN的话,order_num整列都会出现,即使cust_id为NULL。
还有一种全外联接,包含两个表的不关联的行,FULL OUTER JOIN。
带聚集函数的联接
比如要检索每个顾客的下单数
SELECT Customers.cust_id,
Count(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
根据cust_id联接两张表,根据Customers表的cust_id进行分组,并统计每个id的下单数。
使用UNION操作符来组合几条SQL查询。
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
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 ALL则会返回所有结果,包括重复的。
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
在UNION组合查询最后加上ORDERE BY语句,会对返回结果进行排序。
插入数据
插入完整的行
INSERT INTO Customers
VALUES('10000006',
'Toy Land',
'123 Any Street',
'New York',
'NY',
'11111',
'USA',
NULL,
NULL);
将一个新顾客数据插入到表中,每一列数据都要写上,包括NULL值。高度依赖于表中列的定义次序,不安全。
更安全的方法是插入数据时写上对应的列名,即使不同次序也能准确填入。
数据会插入到指定的列中。
提供列名的情况下,也可以仅插入部分列的数据。
比如这里仅插入了7列的数据。
插入检索出的值
从一个表复制到另一个表
SELECT *
INTO CustCopy
FROM Customers;
如果只想复制部分列,则用列名代替*
MariaDB、MySQL、Oracle、PostgreSQL 和 SQLite 使用的语法稍有不同:
CREATE TABLE CustCopy AS
SELECT * FROM Customers;
使用SELECT INTO时,任何SELECT选项和子句都可以使用,可以利用联结从多个表插入数据,不管从多少个表中检索数据,只能插到一个表中。
使用UPDATE和DELETE对数据进行更新和删除。
UPDATE Customers
SET cust_email = '[email protected]'
WHERE cust_id = '1000000005';
更新Custonmers表,将cust_id等于指定值的cust_email列设为指定值。
更新多个列
UPDATE Customers
SET cust_contact = 'Sam Roberts',
cust_email = '[email protected]'
WHERE cust_id = '1000000006';
要删除某个值,可以通过UPDATE将它设为NULL
UPDATE Customers
SET cust_email = NULL
WHERE cust_id = '10000000005';
如果去掉WHERE子句,表示删除cust_email列的所有数据。
用DELETE语句删除数据
DELETE FROM Customers
WHERE cust_id = '100000006';
删除表中符合条件的行。如果省略了WHERE子句,会将表中所有数据删除,不过表还是存在的
如果想删除表中所有行,使用TRUNCATE TABLE语句速度更快。
注意,更新和删除数据时要绝对小心。
创建表
第一列是列名,第二列是类型,第三列是是否接受NULL。第三列允许NULL值的话可以省略。
还可以设定默认值,直接加在NULL值设定后面。
通过ALTER TABLE对表进行修改
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
给指定表新增一列,并设定数据类型。
ALTER TABLE Vendors
DROP COLUMN vend_phone;
删除列vend_phone(此语句并非所有数据库都适用)
小心使用ALTER TABLE。
删除表
DROP TABLE CustCopy;
谨慎使用。
使用视图
视图是虚拟的表,只包含使用时动态检索数据的查询。
举个例子,现在从三个表中检索数据
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 cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
即将中间那块包装成ProductCustomers进行引用,这就是视图的作用,ProductCustomers是一个视图,里面包含一个查询。
使用视图可以对SQL语句进行重用,简化复杂的SQL操作。
利用视图简化复杂的联接
CREATE VIEW ProductCustomers AS
SELECT cust_name,cust_contact,prod_id
FROM Customers,Orders,OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;
此语句创建了一个视图,联结三个表,返回已订购了任意产品的所有顾客的列表。如果执行SELECT*FROM ProductCustomers将列出订购了任意产品的顾客。
如果想要检索订购了产品RGAN01的顾客
SELECT cust_name,cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
视图的另一常见用途是重新格式化检索出的数据。
有时可能会经常使用某个格式的结果
CREATE VIEW VendorLocations AS
SELECT RTRIM(vend_name) + '(' + RTRIM(vend_country) +')' AS vend_title
FROM Vendors;
要检索数据时
SELECT *
FROM VendorLocations;
用视图过滤不想要的数据
CREATE VIEW CustomerEMailList AS
SELECT cust_id,cust_name,cust_email
FROM Customers
WHERE cust_email IS NOT NULL;
选择email非NULL的指定数据,创建视图。
SELECT *
FROM CustomerEMailList
返回email非NULL的指定数据。
使用视图与计算字段
CREATE VIEW OrderItemsExpanded AS
SELECT prod_id,quantity,item_price,quantity*item_price AS expanded_price
FROM OrderItems;
检索订单20008的详细内容
SELECT *
FROM OrderItemsExpanded
WHERE order_num = 20008;
使用存储过程