环境 PostgreSQL 9.6.4
终端 字符集编码 \'gbk\'
chapter01 数据库和 SQL
sudo chown -R postgres PostgreSQL
sudo -u postgres psql -U postgres
create database shop;
\encoding \'gbk\'
\q
sudo -u psql -U postgres -d shop
psql -U user -d dbname
\c dbname
\l
\dt
\d tblname
\di
sql 中
- 等于号是
=, 而不是==; - 单行注释为
--, 多行注释/**/; - 语句结束符为
,
create table Product (
product_id char(4) not null,
product_name varchar(20) not null,
product_type varchar(20) not null,
sale_price integer,
purchase_price integer,
register_date date,
primary key(product_id)
);
drop table Product;
alter table Product add column sales integer;
alter table Product drop column sales;
show client_encoding;
set client_encoding to \'gbk\'
set client_encoding to \'utf8\'
begin transaction;
INSERT INTO Product VALUES (\'0001\', \'T恤\' ,\'衣服\', 1000, 500, \'2009-09-20\');
INSERT INTO Product VALUES (\'0002\', \'打孔器\', \'办公用品\', 500, 320, \'2009-09-11\');
INSERT INTO Product VALUES (\'0003\', \'运动T恤\', \'衣服\', 4000, 2800, NULL);
INSERT INTO Product VALUES (\'0004\', \'菜刀\', \'厨房用具\', 3000, 2800, \'2009-09-20\');
INSERT INTO Product VALUES (\'0005\', \'高压锅\', \'厨房用具\', 6800, 5000, \'2009-01-15\');
INSERT INTO Product VALUES (\'0006\', \'叉子\', \'厨房用具\', 500, NULL, \'2009-09-20\');
INSERT INTO Product VALUES (\'0007\', \'擦菜板\', \'厨房用具\', 880, 790, \'2008-04-28\');
INSERT INTO Product VALUES (\'0008\', \'圆珠笔\', \'办公用品\', 100, NULL, \'2009-11-11\');
commit;
sudo -u postgres pg_ctl -D /usr/local/PostgreSQL/9.6/data/ -w start
sudo -u postgres pg_ctl -D /usr/local/PostgreSQL/9.6/data/ -w restart
sudo -u postgres pg_ctl -D /usr/local/PostgreSQL/9.6/data/ -w stop
exercise
create table Addressbook (
regist_no integer primary key,
name varchar(128) not null,
address varchar(256) not null,
tel_no char(10),
mail_address char(20)
);
alter table Addressbook add column postal_code char(8) not null;
drop table Addressbook;
chapter02 查询基础
2.1
select product_id, product_name, product_type from Product;
-- 查询结果中列的顺序和 select 子句中的顺序相同
select product_type, product_id, product_name from Product;
select * from Product;
select product_id as id, product_name as name, product_type as type from Product;
-- 别名可以使用中文, 使用中文需要双引号括起来
select product_id as "商品编号", product_name as "商品名称", product_type as "商品类型" from Product;
-- select 子句不仅能书写列名, 还可以书写常数
select \'商品\' as String, 66 as number, \'2017-08-20\' as date, product_id from Product;
-- distinct 关键字只能用在第一个关键字前, 将 select 子句的所有字段联合起来判断相等性
select distinct product_type, register_date from product
-- 首先通过 where 子句查询出符合条件的记录, 然后使用 select 子句指定列
select product_name, product_type from product where product_type = \'衣服\';
-- 查询条件可以不显示出来
select product_name from product where product_type = \'衣服\';
2.2
-- 注意要起个别名, 因为之前的字段名字不可用
select sale_price * 2 as double_price from product;
-- 所有包含 null 的计算, 结果肯定是 null
select sale_price * null as multi_null from product;
-- <> 不等号
select product_name, sale_price from product where sale_price <> 500;
select product_name, sale_price, purchase_price from Product where sale_price - purchase_price >= 500;
-- 不能对 null 使用比较运算符, 不管怎么用, 查询结果都为空; SQL 专门用来判断是否为null的是 is null 运算符
select product_name, purchase_price from product where purchase_price is null;
select product_name, purchase_price from product where purchase_price is not null;
2.3
逻辑运算符 not, and, or
- and 运算符的优先级高于 or 运算符
- 善用括号()
select product_name, purchase_price, sale_price from product where sale_price >= 500;
select product_name, purchase_price, sale_price from product where not sale_price >= 500;
select product_name, product_type, register_date from Product
where product_type = \'办公用品\' and register_date = \'2009-09-11\' or register_date = \'2009-09-20\';
select product_name, product_type, register_date from Product
where product_type = \'办公用品\' and (register_date = \'2009-09-11\' or register_date = \'2009-09-20\');
SQL 中逻辑运算时三值逻辑, 真值除了真假之外还有 unknown, 关于 unknown 运算如下
| P | Q | P AND Q |
|---|---|---|
| 真 | unknown | unknown |
| 假 | unknown | 假 |
| unknown | unknown | unknown |
| P | Q | P OR Q |
|---|---|---|
| 真 | unknown | 真 |
| 假 | unknown | unknown |
| unknown | unknown | unknown |
可以看出了 unknown 是一种介于真假之间的逻辑状态
exercise
select product_name, register_date from product where register_date >\'2009-04-28\';
-- SELECT语句①
SELECT product_name, sale_price, purchase_price
FROM Product
WHERE sale_price >= purchase_price + 500;
-- SELECT语句②
SELECT product_name, sale_price, purchase_price
FROM Product
WHERE sale_price - 500 >= purchase_price;
select product_name, product_type, (sale_price * 0.9 - purchase_price) as profit from product
where sale_price * 0.9 - purchase_price > 100 and (product_type = \'办公用品\' or product_type = \'厨房用具\');
chapter03 聚合和排序
3.1
聚合函数中使用 distinct, 删除重复数据
-- count(*) 会得到包含 null 的数据行数, count(<列名>) 会得到 null 之外的数据行数
select count(*) from Product;
select count(*) from Product where product_type = \'衣服\';
select count(distinct product_type) from Product;
select count(purchase_price) from Product;
select sum(sale_price), sum(purchase_price) from Product;
select sum(sale_price), sum(distinct sale_price) from Product;
select avg(sale_price), avg(purchase_price) from Product;
select max(register_date), min(register_date) from product;
3.2
group by 子句的书写位置
select -> from -> where -> group by
上述语句的执行顺序
from -> where -> group by -> select
select product_type, count(product_type) from Product group by product_type;
-- group by 可以包含 null 的分组
select purchase_price, count(*) from Product group by purchase_price;
-- 有 where 子句, 先根据 where 子句指定的条件进行过滤, 然后再进行汇总处理
select purchase_price, count(*) from Product where product_type = \'衣服\' group by purchase_price;
3.3
having 子句必须写在 group by 子句之后, 执行顺序也在 group by 之后
书写规范
- where 子句 = 指定行所对应的条件
- having 子句 = 指定组所对应的条件
select product_type, count(*) from Product group by product_type having count(*) = 2;
3.4
order by 子句书写顺序在 having 之后, 排序时, 默认为升序
ascendent adj 递增的
descendent adj 递减的
DBMS 中, 语句执行顺序
from -> where -> group by -> having -> select -> order by
select * from Product order by sale_price;
select * from Product order by sale_price desc, product_id;
-- 排序键包含 null, 会在开头或者末尾汇总
chapter04 数据更新
4.1
CREATE TABLE ProductIns
(product_id CHAR(4) NOT NULL,
product_name VARCHAR(100) NOT NULL,
product_type VARCHAR(32) NOT NULL,
sale_price INTEGER DEFAULT 0,
purchase_price INTEGER ,
regist_date DATE ,
PRIMARY KEY (product_id));
-- 单条记录插入
insert into ProductIns (product_id, product_name, product_type, sale_price, purchase_price, regist_date) values (\'0001\', \'T恤\' ,\'衣服\', 1000, 500, \'2009-09-20\');
-- 多条记录插入, 记录间用逗号隔开, 语句结尾用分号结束
insert into ProductIns (product_id, product_name, product_type, sale_price, purchase_price, regist_date) values
(\'0002\', \'打孔器\', \'办公用品\', 500, 320, \'2009-09-11\'),
(\'0003\', \'运动T恤\', \'衣服\', 4000, 2800, NULL),
(\'0004\', \'菜刀\', \'厨房用具\', 3000, 2800, \'2009-09-20\'),
(\'0005\', \'高压锅\', \'厨房用具\', 6800, 5000, \'2009-01-15\'),
(\'0006\', \'叉子\', \'厨房用具\', 500, NULL, \'2009-09-20\'),
(\'0007\', \'擦菜板\', \'厨房用具\', 880, 790, \'2008-04-28\');
-- 对表进行全列插入时, 可以省略列清单
insert into ProductIns values (\'0008\', \'圆珠笔\', \'办公用品\', 100, NULL, \'2009-11-11\');
-- default 使用默认值
insert into ProductIns values (\'0009\', \'圆珠笔\', \'办公用品\', default, NULL, \'2009-11-11\');
-- 从其他表复制数据 insert ... select ...
create table ProductCopy (
product_id char(4) not null,
product_name varchar(20) not null,
product_type varchar(20) not null,
sale_price integer,
purchase_price integer,
register_date date,
primary key(product_id)
);
insert into ProductCopy (product_id, product_name, product_type, sale_price, purchase_price, register_date)
select * from product;
create table productType (
product_type varchar(32) not null,
sum_sale_price integer,
sum_purchase_price integer,
primary key (product_type)
);
/*
insert ... select 中的 select 子句可以前面学到的是任意select 查询语句
同样对表进行全列插入时, 可以省略 列清单
*/
insert into productType
select product_type, sum(sale_price), sum(purchase_price) from product group by product_type;
select * from producttype;
4.2
delete 语句删除对象不是表或者列, 而是记录.
delete from ProductCopy;
insert into ProductCopy (product_id, product_name, product_type, sale_price, purchase_price, register_date)
delete from ProductCopy where sale_price >= 4000;
truncate ProductType;
-- 删除列
alter table ProductCopy drop column register_date;
alter table ProductCopy add column register_date date;
update ProductCopy set register_date = \'2017-08-19\';
update ProductCopy set sale_price = sale_price * 10 where product_type = \'厨房用具\';
update ProductCopy set register_date = null where product_id = \'0008\';
-- 多列更新
update Productcopy set sale_price = sale_price / 10, purchase_price = purchase_price + 99 where product_type = \'厨房用具\';
4-4
事务
ACID: 原子性, 一致性, 隔离性, 持久性
begin transaction;
update ProductCopy set register_date = \'2016-08-19\';
commit;
begin transaction;
update ProductCopy set register_date = \'3016-08-19\';
rollback;
exercise
create table ProductMargin (
product_id char(4) not null,
product_name varchar(100) not null,
sale_price integer,
purchase_price integer,
margin integer,
primary key (product_id)
);
insert into ProductMargin (product_id, product_name, sale_price, purchase_price, margin)
select product_id, product_name, sale_price, purchase_price, sale_price - purchase_price from ProductCopy;
update ProductMargin set sale_price = sale_price + 200 where product_name = \'擦菜板\';
update ProductMargin set margin = sale_price - purchase_price where product_name = \'擦菜板\';
chapter05 复杂查询
5.1 视图
create view ProductSum (product_type, count_product)
AS select product_type, count(product_type) from product group by product_type;
-- 多重视图
create view ProductSumJim (product_type, count_product)
AS
select * from ProductSum where product_type = \'办公用品\';
-- cascade 串联
drop view ProductSum cascade;
5.2 子查询
create view ProductSum (product_type, count_product)
AS select product_type, count(*) from product group by product_type;
select product_type, count_product from ProductSum;
-- 等价于
select product_type, count_product from (select product_type, count(*) as count_product from product group by product_type) as ProductSum1;
-- 标量子查询
select product_name, product_type, sale_price from product where sale_price > (select avg(sale_price) from Product);
select product_name, product_type, sale_price, (select avg(sale_price) from Product) from product;
-- 关联子查询 (group by product_type) 可省略
select product_name, product_type, sale_price from product as p1 where sale_price >
(select avg(sale_price) from Product as p2 where p1.product_type = p2.product_type group by product_type);
exercise
create view ViewExer5_1 (product_name, sale_price, register_date)
AS select product_name, sale_price, register_date from Product where sale_price >= 1000 and register_date = \'2009-09-20\';
select * from ViewExer5_1;
insert into ViewExer5_1 values (\'刀子\', 300, \'2009-11-02\');
/*
ERROR: null value in column "product_id" violates not-null constraint
DETAIL: Failing row contains (null, 刀子, null, 300, null, 2009-11-02).
*/
select product_id, product_name, product_type, sale_price, (select avg(sale_price) from product) as sale_price_all from Product;
select product_id, product_name, product_type, sale_price, (
select avg(sale_price) from product as p2 where p1.product_type = p2.product_type
) as sale_price_all
from Product as p1;
chapter06 函数 谓词 case 表达式
5.1 函数
select current_date, current_time, current_timestamp;
select current_timestamp,
extract(year from current_timestamp) as year,
extract(month from current_timestamp) as month,
extract(day from current_timestamp) as day,
extract(hour from current_timestamp) as hour,
extract(minute from current_timestamp) as minute,
extract(second from current_timestamp) as second;
5.2谓词
CREATE TABLE ShopProduct
(shop_id CHAR(4) NOT NULL,
shop_name VARCHAR(200) NOT NULL,
product_id CHAR(4) NOT NULL,
quantity INTEGER NOT NULL,
PRIMARY KEY (shop_id, product_id));
--MySQL
START TRANSACTION;
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000A\', \'东京\', \'0001\', 30);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000A\', \'东京\', \'0002\', 50);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000A\', \'东京\', \'0003\', 15);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000B\', \'名古屋\', \'0002\', 30);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000B\', \'名古屋\', \'0003\', 120);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000B\', \'名古屋\', \'0004\', 20);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000B\', \'名古屋\', \'0006\', 10);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000B\', \'名古屋\', \'0007\', 40);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000C\', \'大阪\', \'0003\', 20);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000C\', \'大阪\', \'0004\', 50);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000C\', \'大阪\', \'0006\', 90);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000C\', \'大阪\', \'0007\', 70);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES (\'000D\', \'福冈\', \'0001\', 100);
COMMIT;
select * from shopproduct;
select product_name, sale_price from product as p
where exists (select * from shopproduct as sp where shop_id = \'000C\' and p.product_id = sp.product_id);
5.3 case 表达式
-- 使用简单CASE表达式的情况
SELECT product_name,
CASE product_type
WHEN \'衣服\' THEN \'A:\' || product_type
WHEN \'办公用品\' THEN \'B:\' || product_type
WHEN \'厨房用具\' THEN \'C:\' || product_type
ELSE NULL
END AS abc_product_type
FROM Product;
--List6-A
--使用查询CASE表达式的情况(代码清单6-41再刊)
SELECT product_name,
CASE WHEN product_type = \'衣服\' THEN \'A:\' || product_type
WHEN product_type = \'办公用品\' THEN \'B:\' || product_type
WHEN product_type = \'厨房用具\' THEN \'C:\' || product_type
ELSE NULL
END AS abc_product_type
FROM Product;