Updated on 2018-04-14
/*2 检索*/
SELECT *
FROM Products;
SELECT prod_name
FROM Products;
-- DISTINCT 去重,作用于所有列
SELECT DISTINCT vend_id
FROM Products;
-- LIMIT 限制数量,OFFSET 偏移数量
SELECT prod_name
FROM Products
LIMIT 5
OFFSET 5;
/*3 排序*/
-- ORDER BY 排序
SELECT prod_name
FROM Products
ORDER BY prod_name;
-- 按多个列排序
SELECT
prod_id,
prod_price,
prod_name
FROM Products
ORDER BY prod_price, prod_name;
-- 等同于
SELECT
prod_id,
prod_price,
prod_name
FROM Products
ORDER BY 2, 3;
-- DESC 降序排序(缺省为升序 ASC)
SELECT
prod_id,
prod_price,
prod_name
FROM Products
ORDER BY prod_price DESC, prod_name;
/*4 过滤*/
-- 检查单个值
SELECT
prod_name,
prod_price
FROM Products
WHERE prod_price = 3.49;
SELECT
prod_name,
prod_price
FROM Products
WHERE prod_price <= 10;
-- 不匹配检查
SELECT
vend_id,
prod_name
FROM Products
WHERE vend_id != 'DLL01';
-- 范围值检查
SELECT
prod_name,
prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;
-- 空值检查
SELECT cust_name
FROM CUSTOMERS
WHERE cust_email IS NULL;
/*5 高级过滤*/
-- AND 且
SELECT
prod_name,
prod_price
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;
-- OR 或
SELECT
prod_name,
prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';
-- NOT 非,条件取反,可以否定(取反)任何条件
SELECT
prod_name,
prod_price
FROM Products
WHERE NOT vend_id = 'FNG01';
-- IN 条件范围,与 OR 功能相当,推荐使用
SELECT
prod_name,
prod_price
FROM Products
WHERE vend_id IN ('DLL01', 'BRS01');
-- () 条件组合,管理比较顺序
SELECT
prod_name,
prod_price
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >= 10;
/*6 通配符*/
-- LIKE 模式匹配,只能用于字符串。出于性能考虑,最好放在过滤条件的最后
-- % 多个字符(任意字符出现任意次数),注意不会匹配 NULL
SELECT
prod_id,
prod_name
FROM Products
WHERE prod_name LIKE '%teddy bear%';
-- _ 单个字符(不多也不少)
SELECT
prod_id,
prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear%';
/*7 创建计算字段*/
-- TRIM() 函数清除左右两边的空格
-- || 拼接字段
-- AS 使用别名
SELECT vend_name || ' (' || vend_country || ')' AS vend_title
FROM Vendors;
SELECT TRIM(vend_name) || ' (' || TRIM(vend_country) || ')' AS vend_title
FROM Vendors;
-- 执行数字计算
SELECT
prod_id,
quantity,
item_price,
quantity * item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;
-- SELECT 语句可以用于测试表达式
SELECT 3 * 2;
SELECT TRIM(' ABC ');
SELECT NOW();
SELECT CURRENT_DATE;
/*8 函数*/
-- 数值函数
-- 系统函数
-- 文本函数
SELECT
vend_name,
UPPER(vend_name) AS vend_name_upcase
FROM Vendors;
-- 时间函数
SELECT order_num
FROM Orders
WHERE DATE_PART('year', order_date) = 2012;
/*9 汇总数据*/
-- 聚集函数
-- COUNT(*) 总行数
-- COUNT(column) 该列的行数(忽略 NULL)
-- SUM(column) 该列的总和
-- AVG(column) 该列的平均值
-- MAX(column) 该列的最大值
-- MIN(column) 该列的最小值
-- 聚集不同值
-- DISTINCT 去重(缺省为所有行 ALL)
SELECT
COUNT(DISTINCT prod_price) AS num_price_dist,
COUNT(prod_price) AS num_price,
AVG(DISTINCT prod_price) AS avg_price_dist,
AVG(prod_price) AS avg_price,
MAX(prod_price) AS max_price,
MIN(prod_price) AS min_price,
SUM(prod_price) AS sum_price
FROM Products
WHERE vend_id = 'DLL01';
SELECT SUM(item_price * quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;
/*10 分组数据*/
-- 创建分组
-- GROUP BY 子句指示 DBMS 进行数据分组,然后对每个组进行聚集,而不是对整个结果集
-- GROUP BY x 根据 x 列进行分组
-- GROUP BY x, y 根据 x 列和 y 列进行分组(将 x 列和 y 列的值视为一个整体来分组)
SELECT
vend_id,
COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
-- 等同于
SELECT
vend_id,
COUNT(*) AS num_prods
FROM Products
GROUP BY 1;
-- 过滤分组
-- WHERE 过滤行,
-- HAVING 过滤组;
-- WHERE 在数据分组前进行过滤,
-- HAVING 在数据分组后进行过滤。
SELECT
vend_id,
COUNT(*) AS num_prods -- 计数
FROM Products
WHERE prod_price >= 4 -- 过滤(行级)
GROUP BY 1 -- 分组
HAVING COUNT(*) >= 2 -- 过滤(组级)
ORDER BY num_prods, 1 DESC; -- 排序
/*11 子查询*/
-- 子查询:嵌套在其他查询中的查询,只能查询单个列,总是由内向外进行处理
-- 子查询性能不好,推荐使用联结(JOIN)
-- 填充 IN 操作符
-- 显示订购产品 RGAN01 的顾客列表
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';
-- +
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');
-- +
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'));
-- 填充计算列
SELECT COUNT(*) AS orders
FROM Orders
WHERE cust_id = '1000000001';
-- +
SELECT
cust_name,
cust_state
FROM Customers;
-- =
SELECT
cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders -- 避免歧义
FROM Customers;
/*12 联结表*/
-- 联结机制:在一条 SELECT 语句中关联多个表中配对成功的行,返回一组输出
-- 联结方式:首先列出所有表,然后定义表之间的配对条件
-- 注意:
-- ON 只用于连接行(联结)
-- WHERE 只用于过滤行
-- 显示订购产品 RGAN01 的顾客列表
-- 等值联结(内联结)
SELECT
cust_name,
cust_contact
FROM Orders, Customers, OrderItems
WHERE prod_id = 'RGAN01'
AND Orders.order_num = OrderItems.order_num
AND Orders.cust_id = Customers.cust_id;
-- 简单格式
SELECT
vend_name,
prod_name
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
-- 等同于
-- 标准格式
SELECT
vend_name,
prod_name
FROM Vendors
-- 联结机制
INNER JOIN Products
ON Vendors.vend_id = Products.vend_id; -- 配对条件
/*13 高级联结*/
-- 自联结(self-join)
-- 多次引用相同的表
SELECT
c1.cust_id,
c1.cust_name,
c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c2.cust_contact = 'Jim Jones'
AND c1.cust_name = c2.cust_name;
-- 自然联结(natural join)
-- 等值联结(内联结)的特例化
-- 联结时,自动比较名称相同的列
-- 输出时,自动去除名称相同的列
SELECT *
FROM Vendors
NATURAL JOIN Products;
-- 对比
SELECT *
FROM Vendors
INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
SELECT
cust_name,
cust_contact
FROM Orders
NATURAL JOIN Customers
NATURAL JOIN OrderItems
WHERE prod_id = 'RGAN01';
-- 外联结(outer join)
-- 全外联结:内联结 + 全部表没有配对成功的行(FULL)
-- 左外联结:内联结 + 左边表没有配对成功的行(LEFT)
-- 右外联结:内联结 + 右边表没有配对成功的行(RIGHT)
-- 左外联结和右外联结可以交换使用,区别在于关联表的顺序
SELECT
Customers.cust_id,
Orders.order_num
FROM Customers
LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
-- 等同于
SELECT
Customers.cust_id,
Orders.order_num
FROM Orders
RIGHT OUTER JOIN Customers
ON Customers.cust_id = Orders.cust_id;
-- 带有聚集函数的联结
SELECT
Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers
LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id
ORDER BY 2 DESC;
/*14 组合查询*/
-- 并(union):执行多条 SELECT 语句,并合并为一个结果集
-- UNION 和 WHERE 的多个 OR 条件所完成的工作相同
-- UNION 无重复行,
-- UNION ALL 有重复行。
SELECT
cust_name,
cust_contact
FROM Customers
WHERE cust_state IN ('IL', 'IN', 'MI')
UNION
SELECT
cust_name,
cust_contact
FROM Customers
WHERE cust_name = 'Fun4All';
-- 等同于
SELECT
cust_name,
cust_contact
FROM Customers
WHERE cust_state IN ('IL', 'IN', 'MI')
OR cust_name = 'Fun4All';
/*15 插入数据*/
-- 插入完整行
INSERT INTO Customers
VALUES ('1000000006', 'Toy Land', '123 Any Street', 'New York', 'NY', '11111', 'USA',NULL,NULL);
-- 插入部分行
INSERT INTO Customers (cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip)
VALUES ('1000000006', 'Toy Land', '123 Any Street', 'New York', 'NY', '11111');
-- 导入行(INSERT SELECT)
INSERT INTO Customers (cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip)
SELECT
cust_id,
cust_name,
cust_address,
cust_city,
cust_state,
cust_zip
FROM CustNew;
-- 导出行(SELECT INTO)
SELECT *
INTO CustNew
FROM Customers;
/*16 更新和删除数据*/
-- 更新指定行
UPDATE Customers
SET cust_contact = 'Sam Roberts',
cust_email = 'sam@toyland.com'
WHERE cust_id = '1000000006';
UPDATE account
SET "role" =
CASE "role"
WHEN 'USER1' THEN 'STAFF1'
WHEN 'USER2' THEN 'STAFF2'
ELSE "role"
END;
-- 删除指定行
DELETE FROM Customers
WHERE cust_id = '1000000006';
-- 删除所有行
TRUNCATE TABLE Customers;
/*17 创建和操纵表*/
/*18 视图*/
-- 一张虚拟表,包含的不是数据,而是 SELECT 语句,用于动态检索数据,可以重用 SQL,简化复杂数据的处理
-- 创建视图
-- 用于封装复杂的查询语句
CREATE OR REPLACE 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;
-- 用于简化计算字段
CREATE OR REPLACE VIEW OrderItemsExpanded AS
SELECT
prod_id,
quantity,
item_price,
quantity * item_price AS expanded_price,
order_num
FROM OrderItems;
-- 用于格式化检索出的数据
CREATE OR REPLACE VIEW VendorLocations AS
SELECT TRIM(vend_name) || ' (' || TRIM(vend_country) || ')'
AS vend_title
FROM Vendors;
-- 使用视图
SELECT *
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
SELECT *
FROM OrderItemsExpanded
WHERE order_num = 20008;
SELECT *
FROM VendorLocations;
-- 删除视图
DROP VIEW ProductCustomers;
/*19 存储过程*/
/*20 事务处理*/
/*21 游标*/
/*22 SQL 特性*/
-- 约束:管理如何插入或处理数据库数据
-- 主键
ALTER TABLE Vendors
ADD PRIMARY KEY (vend_id);
-- 外键
ALTER TABLE Orders
ADD FOREIGN KEY (cust_id) REFERENCES Customers (cust_id);
-- 检查约束
ALTER TABLE OrderItems
ADD CHECK (quantity > 0);
-- 唯一约束
-- 索引:改善检索数据的性能
-- 用于对列进行过滤和排序
-- 取值范围越大的列做索引效果越好,比如姓名列就比性别列收益高
CREATE INDEX prod_name_ind
ON Products (prod_name);
-- 触发器:特殊的存储过程,在单表发生特定动作时自动执行
-- INSERT 时能够访问新数据
-- UPDATE 时能够访问新数据和旧数据
-- DELETE 时能够访问删除的数据
CREATE TABLE emp (
empname TEXT,
salary INTEGER,
last_user TEXT,
last_date TIMESTAMP
);
CREATE OR REPLACE FUNCTION emp_stamp()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.empname IS NULL
THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL
THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
IF NEW.salary < 0
THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- 记录最后操作的人和日期
NEW.last_user := current_user;
NEW.last_date := current_timestamp;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp
BEFORE INSERT OR UPDATE
ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();