《SQL必知必会》笔记

数据查询

SELECT
作用:从一个或多个表(试图)中检索数据

子句顺序

《SQL必知必会》笔记_第1张图片

检索不同的值 DISTINCT

注意:查询的结果列中不能部分使用distinct。distinct 关键字作用于所有的列,不仅仅是跟在其后的那一列。
示例:
数据表:
《SQL必知必会》笔记_第2张图片
select distinct vend_id,prod_price from products;
返回:
《SQL必知必会》笔记_第3张图片

限制查询数量

返回前5行数据

(SQL SERVER)select TOP 5 ... from ...
(DB2) select ... from ... FETCH FRIST 5 ROWS ONLY;
(Oracle)select ... from ... where ROWNUM <=5;
(MYSQL等)select ... from ... LIMIT 5;

返回后5行数据

-- 从第5行起的5行数据
select ... from ... limit 5 offset 5;

WHERE 子句操作符

《SQL必知必会》笔记_第4张图片
《SQL必知必会》笔记_第5张图片

创建计算字段

拼接字符

+, ||, concat。不同数据库不一样

(SQL SERVER)select vend_name + '(' + vend_country + ')';
(DB2、pg、oracle) select vend_name || '(' || vend_country || ')';
(MYSQL)select concat(vend_name , '(' , vend_country , ')');

去掉字符空格

Rtrim(), Ltrim(),trim() 
--去掉右边空格
select Rtrim(vend_name)
--去掉左边空客
select Ltrim(vend_name)
-- 去掉两边的空格
select trim(vend_name)

使用别名AS

注意:别名的名字即可以是一个单词,也可以是一个字符串。如果是后者,字符串需要括在引号中。一般不建议使用字符串作为别名,会给客户端应用带来各种问题。因此,别名最常见的使用是将多个单词的列名重命名为一个单词的名字。

执行算数计算

select order_num,
	   sum( item_price * quantity ) as order_amount
from order_items

如果省略了from子句,select就是简单的访问和处理表达式,比如’select 3*2’会返回6

聚集函数

函数 含义 备注
AVG() 平均值 忽略NULL值
MAX() MIN() 最大值/最小值 忽略NULL值
COUNT 特定列中具有值的行进行计数 Count(column)忽略NULL值,Count(*)包含NULL值
SUM 求和 忽略NULL值

注意
所有聚集函数都可以使用[distinct],如COUNT(distinct product_name)
利用标准的算数操作符,所有聚集函数都可用来执行多个列上的计算,如avg(item_pricequantity)
所有聚集函数指定列名时都会忽略NULL值的行,count(
)则不会忽略

分组查询

使用分组可以将数据分为多个逻辑组,对每个组进行聚集计算。

创建分组

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 子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果select中使用表达式,则必须在group by子句中指定相同的表达式,不能使用别名。
大多数sql实现不允许group by 列带有长度可变的数据类型。
除聚集计算语句外,select语句中的每一列都必须在group by 子句中给出。
如果分组列中包含具有null值的行,则null将作为一个分组返回。

过滤分组

语句:HAVING
having是基于完整的分组而不是个别的行进行过滤的。是对select语句中的聚集函数进行过滤。

为什么select语句中的每一列都必须在group by子句中给出?

因为被分到一个组的数据的其他列的值不一定都相等,数据库不知道该返回什么。除非返回的列的值也是每个分组中的值都一样。
比如products表中的数据如下:

此时查询每个供应商提供的产品个数,3个供应商分为3组,应该为:
select vend_id ,count(*) as num_prods from products GROUP BY vend_id
如果返回的列增加一个:prod_price
select vend_id ,prod_pric,count(*) as num_prods from products GROUP BY vend_id
此时在每个分组里,prod_price的值是不一样的,数据库不知道该返回什么,就会报错。如果额外的列在同一个分组里值都一样,就不会报错。
mysql的ONLY_FULL_GROUP_BY错误
注意:如果select中返回的列不在group by中mysql会报ONLY_FULL_GROUP_BY错误,可以在列上使用any_value(),数据库会选择被分到同一组的数据里第一条数据的指定列值作为返回数据。
ONLY_FULL_GROUP_BY的语义就是确定select target list中的所有列的值都是明确语义。在此模式下,target list中的值要么是来自于聚合函数(sum、avg、max等)的结果,要么是来自于group by list中的表达式的值
MySQL提供了any_value()函数来抑制ONLY_FULL_GROUP_BY值被拒绝
any_value()会选择被分到同一组的数据里第一条数据的指定列值作为返回数据

子查询

利用子查询过滤

-- 查询购买过单价>10的产品的顾客信息
select cust_id,customers.cust_name,cust_contact
from customers
where cust_id in
	(select DISTINCT cust_id from orders where order_num in 
		(SELECT order_num from order_items WHERE quantity >10)
	);

注意:
由于性能限制,不能嵌套太多的子查询
子查询的select语句只能查询单个列

子查询作为计算字段

/** 示例:查询每个顾客的订单总数
1.从customers表中检索顾客列表
2.对于检索出的每个顾客,统计其在orders表中的订单数目
**/
select cust_name,cust_state,
		(select count(*) 
         from orders 
		 where orders.cust_id = customers.cust_id) as Orders
from customers

联结表

联结方式

联结符 含义
left join /left outer join 左外联结:包含左表的所有行,对应的右表行可能为空。
right join/left outer join 右外联结:包含右表的所有行,对应的左表行可能为空。
join/inner join 内联结:只包含左右表都匹配并且不为空的行 (与join相同)。建立的每个内联结都是自然联结。
-- 联结多个表
-- 方式一
select cust_name,
	orders.order_num,
	sum(item_price * quantity) as orderTotal
from customers 
join orders on orders.cust_id = customers.cust_id
join order_items on orders.order_num = order_items.order_num
group by cust_name, orders.order_num
order by customers.cust_id,orders.order_num

-- 方式二
select cust_name,
	orders.order_num,
	sum(item_price * quantity) as orderTotal
from customers ,orders,order_items
where orders.cust_id = customers.cust_id
	and orders.order_num = order_items.order_num
group by cust_name, orders.order_num
order by customers.cust_id,orders.order_num

使用注意

  • 在联结两个表时,实际要做的是将第一个表中的每一行与第二个表中的每一行配对。
  • 在联结表时,如果不添加WHERE过滤,将会返回表关系的笛卡尔积,存在很多重复数据。
  • WHERE可以限定只返回那些匹配给定条件(这里是联结条件)的行,要保证所有联结都有WHERE子句。
  • 许多DBMS处理联结远比处理子查询快的多。

组合查询

UNION使用场景

在一个查询中从不同的表返回结构数据
对一个表执行多个查询,按一个查询返回数据
组合查询和where
任何具有多个where子句的select语句都可以作为一个组合查询
创建组合查询

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state  = 'MI'
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state = 'IL'
ORDER BY cust_name;

UNION使用注意

  • union中每个查询必须包含相同的列、表达式或聚集函数(顺序不必一致) 列数据类型必须兼容 必须由两条或两条以上的select语句组成
  • union从查询结果集中自动去除了重复的行,如果不需要去除重复,可使用UNION ALL
  • 在用UNION组合查询时,只能使用一条order by 子句,它必须位于最后一条select语句之后。

其他类型的组合

EXCEPT(MINUS): 可用来检索只在第一个表中存在而在第二个表中不存在的行.
INTERSECT: 可用来检索两个表中都存在的行
数据crud
UPDATE
作用:更新表中的一行或多行数据
update tablename
set columname = value, …
[where …]
INSERT
作用:添加一行数据
insert into tablename [(column1,…)]
values(value1,…)
DELETE
作用:删除一行或多行数据
delete from tablename
[where …];
INSERT SELECT
作用:将select的结果插入到一个表
insert into tablename [(column1,…)]
select columns, … from tablename, …
[where …];
数据库操作
CREATE TABLE
作用:创建表
DROP TABLE IF EXISTS order_items;
CREATE TABLE order_items (
order_num varchar(255) NOT NULL,
order_item varchar(255) NOT NULL,
prod_id varchar(255) NOT NULL,
quantity varchar(255) NOT NULL,
item_price varchar(255) DEFAULT NULL,
PRIMARY KEY (order_num,order_item),
foreign key(order_num) references orders(order_num),

foreign key(prod_id) references products(prod_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
SET FOREIGN_KEY_CHECKS = 1;
ALTER TABLE
作用:更新已存在表的结构

alter table tablename
(
  add|drop column datatype [null|not null] [constraints],
  add|drop column datatype [null|not null] [constraints],
  ...
);

COMMIT
作用:用来将事物写入数据库
commit [transaction]
CREATE INDEX
作用:用于在一个或多个列上创建索引

create index indexname
on tablename (column, ...)

CREATE PROCEDURE
作用:用于创建存储过程

commit [transaction]

CREATE VIEW
作用:用于创建一个或多个表上的新试图

create view viewnamne as
select columns, ...
from tables, ...
[where ...]
[group by ...]
[having ...];

DROP
作用:永久的删除数据库对象(表、试图、索引等)

drop index|tablename|viwe indexname|table|view name|...

ROLLBACK
作用:用于撤销一个事物块

rollback [TO savepointname]

练习题
/练习题10-1:查询每个订单号各有多少行/

select order_num ,count(*) as order_lines
from order_items
GROUP BY order_num
ORDER BY order_lines;

/练习题10-2:查询每个供应商成本最低的商品,并按成本价排序/

select vend_id,min(prod_price) as cheapest_item 
from products
group by vend_id
order by cheapest_item desc;

/练习题10-3:查询最佳顾客:至少含有100个item的所有订单/

select order_num, count(*) as order_lines
from order_items
GROUP BY order_num
HAVING order_lines>3;

/练习题10-4:查询最佳顾客,订单总价值>1000的订单/

select order_num,sum(item_price*quantity) as order_amount
from order_items
GROUP BY order_num
HAVING order_amount>1000;

/**解决sql_mode=only_full_group_by
set session sql_mode=‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION’;
select version(), @@sql_mode;
**/

/练习题11-1:查询购买过单价>10的产品的顾客信息/

select cust_id,customers.cust_name,cust_contact
from customers
where cust_id in
	(select DISTINCT cust_id from orders where order_num in 
		(SELECT order_num from order_items WHERE quantity >10)
	);

/练习题11-2: 查询购买了prod_id为BR01的产品的顾客ID和订单日期/

select cust_id
from orders
where order_num in
	(select DISTINCT order_num from order_items where prod_id = 'BR01');
select cust_id,order_date
from orders join order_items on orders.order_num = order_items.order_num
where order_items.prod_id ='BR01';

/练习题11-3: 查询购买了prod_id为BR01的产品的顾客邮件/

select cust_id,customers.cust_name,cust_email
from customers
where cust_id in
	(select DISTINCT cust_id from orders where order_num in
		(select DISTINCT order_num from order_items where prod_id = 'BR01'));
select customers.cust_id,customers.cust_name,cust_email
from customers
join orders on customers.cust_id = orders.cust_id 
join order_items on orders.order_num = order_items.order_num
where order_items.prod_id ='BR01';

/*练习题11-4: 查询顾客的已订购金额
1.总额的查询:查询每个订单的总额,然后根据顾客ID对订单分组再计算总额
*/

select orders.cust_id,
	sum(
		(select sum(quantity*item_price) 
		 from order_items 
		 where orders.order_num = order_items.order_num 
		 group by order_num) 
	)as order_amount
from orders
group by orders.cust_id
order by order_amount desc

/练习题11-5: 从products表中检索所有的产品名称,以及其已经销售的总数quant_sold/

select products.prod_name,
	(select sum(quantity) 
		 from order_items 
		 where products.prod_id= order_items.prod_id
		 group by prod_id
	) as quant_sold
from products 

/练习题12-2查询顾客名称和订单号,以及订单总价,按顾客名称和订单号排序/

-- 个人解答:包含未下单的顾客
-- 方法一:比较笨的方法
select customers.cust_id,customers.cust_name ,ot.order_num,ot.orderTotal
from customers 
left join 
	(select orders.order_num,cust_id,orderTotal
	from orders
	inner join 
		(select order_num,sum(item_price * quantity) as orderTotal
		from order_items 
		group by order_num) as order_total
	on orders.order_num = order_total.order_num) as ot
on customers.cust_id = ot.cust_id
order by customers.cust_id,ot.order_num;
-- 查看答案后改的方法,重点:子查询用于计算公式中
select customers.cust_id,customers.cust_name ,ot.order_num,ot.orderTotal
from customers 
left join 
	(select order_num,
			cust_id,
		    (select sum(item_price * quantity) from order_items  where order_items.order_num = orders.order_num) as orderTotal 
	from orders) as ot
on customers.cust_id = ot.cust_id
order by customers.cust_id,ot.order_num
-- 查看答案后改的方法,重点:group by 可以使用多个列,多个表左关联
select cust_name,
	orders.order_num,
	sum(item_price * quantity) as orderTotal
from customers 
left join orders on orders.cust_id = customers.cust_id
left join order_items on orders.order_num = order_items.order_num
group by cust_name, orders.order_num
order by customers.cust_id,orders.order_num

– 官网答案:不包含没有下单的顾客

-- Solution using subqueries
SELECT cust_name,
       order_num,
       (SELECT Sum(item_price*quantity)
       FROM order_items
       WHERE orders.order_num=order_items.order_num) AS OrderTotal
FROM customers, orders
WHERE customers.cust_id = Orders.cust_id
ORDER BY cust_name, order_num;
-- Solution using joins, 不包含没有下单的顾客
SELECT cust_name,
       orders.order_num,
       Sum(item_price*quantity) AS OrderTotal
FROM customers, orders, order_items
WHERE customers.cust_id = Orders.cust_id
 AND orders.order_num = order_items.order_num
GROUP BY cust_name, orders.order_num
ORDER BY cust_name, order_num;

/练习题12-5:查询最佳顾客,订单总价值>1000的顾客信息/

select order_num,sum(item_price*quantity) as order_amount
from order_items
GROUP BY order_num
HAVING order_amount>1000;
select  customers.cust_id, customers.cust_name, orders.order_num, sum(item_price*quantity) as order_amount
from customers,orders,order_items
where customers.cust_id = orders.cust_id and order_items.order_num = orders.order_num
GROUP BY customers.cust_id,orders.order_num
HAVING order_amount>1000;

/嵌套查询 与 自联结 注意:许多数据库处理联结比处理子查询快的多/

/**给与Jim Jones同公司的所有顾客发礼物:

  1. 找到Jim Jones的公司
  2. 查询与公司是步骤1的顾客
    **/
-- 自联结实现
select c1.cust_id, c1.cust_company,c1.cust_name,c2.cust_company,c2.cust_name
from customers as c1
INNER join customers as c2
ON c1.cust_company = c2.cust_company
and c2.cust_name='Jim Jones'
order by c1.cust_company;
-- 嵌套查询实现
select cust_id, cust_company,cust_name
from customers 
where cust_company = (select cust_company from customers where cust_name='Jim Jones')
order by cust_name;

/** 练习题13-4: 查询每一项产品的总订单数量**/

select products.prod_id,prod_name, any_value(item_price),avg(item_price),sum(quantity) as total
from products left join order_items 
on products.prod_id = order_items.prod_id
GROUP BY products.prod_id
ORDER BY products.prod_name;

/** 练习题13-5: 查询每一项产品的总订单数量**/

select vend_id, (select count(prod_id) from products where vendors.vend_id = products.vend_id) 
from vendors;

SELECT Vendors.vend_id, COUNT(prod_id)
FROM Vendors
LEFT OUTER JOIN Products ON Vendors.vend_id = Products.vend_id
GROUP BY Vendors.vend_id;

依赖数据
数据库表结构
《SQL必知必会》笔记_第6张图片

创建表和数据sql

书简介
Sams Teach Yourself SQL in 10 Minutes (Fifth Edition) – Ben Forta

你可能感兴趣的:(计算机基础,sql,数据库)