USE sql_store; -- 或者双击选中 SELECT*-- return all columns FROM customers;
SELECT first_name, last_name FROM customers;
WHERE
1 2 3 4 5 6 7 8
SELECT*FROM customers WHERE customer_id=1ORDERBY first_name; -- order of each clause matters SELECT first_name, points, (points+10)*100AS'discount factor' FROM customers; SELECTDISTINCT state FROM customers; -- 不重复 SELECTDISTINCT state FROM customers WHERE state!='va'; -- 大小写不敏感
AND, OR, NOT
1 2 3
-- 优先级 and or -- WHERE NOT (birth_date >'1990-01-01' OR points <=1000) 依次取反 -- WHERE birth_date <='1990-01-01' AND points > 1000
IN
1
SELECT*FROM custormers WHERE state IN ('VA','FL','GA'); -- 一个属性与列表比较
BETWEEN
1
SELECT*FROM customers WHERE birth_date BETWEEN'1990-01-01'AND'2000-01-01'; -- 包含两端
LIKE 模糊查找
1 2 3 4 5
SELECT*FROM customers WHERE last_name LIKE'b%'; '%b%' 'b____y' SELECT*FROM customers WHERE address LIKE'%trail%'OR address LIKE'%AVENUE%'; SELECT*FROM customers WHERE phone NOTLIKE'%9'
% : any character (zero or more)
_: one character
REGEXP 通配符
1 2 3 4
SELECT*FROM customers WHERE last_name REGEXP 'field' 'field$|mac|ch' '[gim]e' ge,ie,me '[a-h]e'
^: begin with
$: end with
|: logical or/ multiple search pattern
[ ]: single character in
IS NULL
1
SELECT*FROM orders WHERE shipped_date ISNULL; -- is NOT NULL
primary key: unique and
ORDER BY 排序
sql可以用任意列排序,不管是否选中了该列
1
SELECT*, unit_price*quantity as total_price FROM order_items WHERE order_id=2ORDERBY total_price DESC;
LIMIT
1 2 3
SELECT*FROM customers LIMIT 300; SELECT*FROM customers LIMIT 6, 3;-- offset, num to show; show 7.8.9 SELECT*FROM customers ORDERBY points DESC LIMIT 3; -- LIMIT 顺序永远在最后
3. 在多个表中检索数据
Inner Joins
1 2 3 4 5
SELECT*FROM orders JOIN customers ON orders.customer_id=customers.customer_id; -- 默认inner join
SELECT order_id, o.customer_id, first_name, last_name FROM orders o JOIN customers ON o.customer_id=customers.customer_id; -- use alias to simplify
Joining across database
1 2
SELECT*FROM order_items oi JOIN sql_inventory.products p ON oi. product_id=p.product_id; -- 跨库检索
Self Join
1 2 3
SELECT e.employee_id,e.first_name, m.first_name AS manager FROM employees e JOIN employees m ON e.reports_to=m.employee_id; -- self join
Joining multiple tables
1 2 3 4
SELECT p.date, p.invoice_id, p.amount, c.name as client_name, pm.name as payment_method FROM payments p JOIN clients c ON p.client_id=c.client_id JOIN payment_methods pm ON p.payment_method=pm.payment_method_id;
Compound join conditions
复合主键 composite primary key: 包含多个主键
1
JOIN ... ON ... AND...
Implicit JOIN
用explicit比较好!
1 2 3 4 5 6 7
SELECT*FROM orders JOIN customers ON orders.customer_id=customers.customer_id;
-- implicit JOIN SELECT* FROM orders o, customers c WHERE o.customer_id=c.customer_id;
Outer Joins
LEFT JOIN: 无论是否匹配都返回左表的记录
1 2 3 4
SELECT p.product_id, p.name, oi.quantity FROM products p LEFTJOIN order_items oi ON p.product_id=oi.product_id; -- outer join
outer join between multiple tables
1 2 3 4 5 6 7 8
SELECT o.order_date, o.order_id, c.first_name, s.name as shipper, os.name as status FROM orders o JOIN customers c ON o.customer_id=c.customer_id LEFTJOIN shippers s ON o.shipper_id=s.shipper_id JOIN order_statuses os ON o.status=os.order_status_id
Self Outer JOIN
Using Clause
如果列名一致
1 2 3 4 5 6 7 8 9 10 11 12
SELECT* FROM orders o JOIN customers c USING (customer_id); -- ON o.customer_id=c.customer_id
SELECT p.date, c.name, p.amount, pm.name FROM payments p JOIN clients c USING (client_id) JOIN payment_methods pm ON p.payment_method=pm.payment_method_id;
Natural Join
1 2 3
SELECT* FROM orders o NATURALJOIN customers c; -- 自动匹配相同列;不建议使用
-- CROSS JOIN -- explicit SELECT sh.name AS shipper, p.name AS product FROM shippers sh CROSSJOIN products p;
-- implicit SELECT sh.name AS shipper, p.name AS product FROM shippers sh ORDERBY sh.name
Unions
组合查询结果,注意列数要一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
SELECT customer_id, first_name, points, 'Bronze'AS type FROM customers WHERE points<2000 UNION SELECT customer_id, first_name, points, 'Silver'AS type FROM customers WHERE points between2000AND3000 UNION SELECT customer_id, first_name, points, 'Gold'AS type FROM customers WHERE points>=3000 ORDERBY first_name
CREATETABLE invoices_achived AS SELECT i.invoice_id, i.number, c.name, i.invoice_total, i.payment_total, i.invoice_date, i.due_date, i.payment_date FROM invoices i JOIN clients c ON c.client_id=i.client_id WHERENOT (payment_date ISNULL) ;
Update
update single row
1 2 3
UPDATE invoices SET payment_total=100 WHERE invoice_id=6;
update multiple row
1 2 3
UPDATE customers SET points=points+50 WHERE birth_date<'1990-01-01';
subquery, 多条件用in,单条件用=
1 2 3 4 5 6
UPDATE orders SET comments='Gold customer' WHERE customer_id IN (SELECT customer_id FROM customers WHERE points>3000);
Delete
1 2 3 4 5 6
DELETEFROM invoices WHERE client_id=( SELECT client_id FROM clients WHERE name='Myworks' )
5.聚合函数
1 2 3 4 5 6 7 8 9 10 11
MAX() MIN() AVG() SUM() COUNT() --作用于所有非空记录 SELECT MAX(invoice_total) AS highest COUNT(*) AS total_records --所有记录 COUNT(DISTINCT client_id) AS total_clients FROM invoices WHERE invoice_date<'2019-07-01'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
SELECT 'Fist half of 2019'AS date_range, SUM(invoice_total) AS total_sales, SUM(payment_total) AS total_payments, SUM(invoice_total) -SUM(payment_total) AS what_we_expect FROM invoices WHERE invoice_date<'2019-07-01' UNION SELECT 'Second half of 2019'AS date_range, SUM(invoice_total) AS total_sales, SUM(payment_total) AS total_payments, SUM(invoice_total- payment_total) AS what_we_expect FROM invoices WHERE invoice_date BETWEEN'2019-01-01'AND'2019-07-01';
Group By
1 2 3 4 5 6 7 8 9
SELECT p.date, pm.name AS payment_method, SUM(p.amount) AS total_payments FROM payments p JOIN payment_methods pm ON p.payment_method=pm.payment_method_id GROUPBY p.date, payment_method ORDERBY p.date;
Having
1 2 3 4 5 6 7 8
-- having 在group by后筛选,用到的列必须是select中选中的列, where用到的列可以是任意列 SELECT client_id, SUM(invoice_total) AS total_sales, COUNT(*) AS number_of_invoices FROM invoices GROUPBY client_id HAVING total_sales >500AND number_of_invoices >5;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
SELECT c.customer_id, c.first_name, -- oi.order_id, SUM(oi.quantity*oi.unit_price) AS total_sales FROM orders o JOIN order_items oi USING (order_id) JOIN customers c USING (customer_id) WHERE c.state='VA' GROUPBY-- 使用了聚合函数,group by要包含所有field c.customer_id, c.first_name HAVING total_sales>100;
With Rollup
1 2 3 4 5 6 7 8 9
-- with rollup 汇总 SELECT pm.name AS payment_method, SUM(p.amount) AS total FROM payments p JOIN payment_methods pm ON p.payment_method=pm.payment_method_id GROUPBY pm.name WITHROLLUP-- 有with rollup的话不能用别名 ORDERBY total;
6.复杂查询
子查询
1 2 3 4 5 6 7 8 9 10 11 12
SELECT* FROM employees WHERE salary > (SELECTAVG(salary) FROM employees);
-- 子查询返回多个值 USE sql_invoicing; SELECT* FROM clients WHERE client_id NOTIN ( SELECTDISTINCT client_id FROM invoices );
-- subquery USE sql_store; SELECT customer_id,first_name,last_name FROM customers c WHERE customer_id IN ( SELECTDISTINCT customer_id FROM order_items oi JOIN orders o USING (ORDER_ID) WHERE product_id=3 );
-- join USE sql_store; SELECTDISTINCT customer_id,first_name,last_name FROM customers c JOIN orders o USING (customer_id) JOIN order_items oi USING (order_id) WHERE oi.product_id=3;
ALL, ANY, SOME — > ALL() 相当于 > MAX() — IN () 相当于 = ANY()
CORRELATED SUB-QUERY
1 2 3 4 5 6 7 8
-- CORRELATED SUB-QUERY 在主查询的每条记录都进行一次相关子查询 USE sql_invoicing; SELECT* FROM invoices i WHERE invoice_total> ( SELECTAVG(invoice_total) FROM invoices WHERE client_id=i.client_id);
Exists
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-- exists 不将所有结果返回外部查询,避免返回大 结果集,适合多结果的集合 SELECT* FROM clients c WHEREEXISTS ( SELECT client_id FROM invoices WHERE client_id=c.client_id ); USE sql_store; SELECT* FROM products p WHERENOTEXISTS ( SELECT product_id FROM order_items WHERE product_id=p.product_id );
在select中使用子查询
1 2 3 4 5 6 7
-- 复杂查询,在select中使用子查询 SELECT client_id, name, (SELECTSUM(invoice_total) FROM invoices WHERE client_id=c.client_id) AS total_sales, (SELECTAVG(invoice_total) FROM invoices) AS average, (SELECT total_sales) -(SELECT average) AS difference FROM clients c;
-- if() SELECT oi.product_id, p.name, count(*) AS orders, IF(COUNT(*)=1,'Once','Many times') AS frequency FROM order_items oi JOIN products p USING (product_id) GROUPBY oi.product_id, p.name
case when… then…
1 2 3 4 5 6 7 8 9 10
-- case when ... then ... SELECT CONCAT(first_name,' ',last_name) AS customer, points, CASE WHEN points>3000THEN'Gold' WHEN points BETWEEN2000AND3000THEN'silver' WHEN points<2000THEN'bronze' ENDAS category FROM customers ORDERBY points DESC;
8.视图
创建视图
跟表格一样使用
1 2 3 4 5 6 7 8
CREATEVIEW clients_balance AS SELECT i.client_id AS client_id, c.name AS name, SUM(i.invoice_total-i.payment_total) AS balance FROM invoices i JOIN clients c USING (client_id) GROUPBY client_id;
updatable view:
Distinct
sum, min, max
group by
union
views的好处
简化查询
减少修改带来的影响(例如修改原表的列名后,可以通过view修改,这样后续查询不受影响-类似函数
防止对源数据的访问
不要在存储过程中加入太多逻辑验证,难以管理。在程序中验证输入。
函数只能返回单一值
函数特性:
deterministic reads sql data modifies sql data
9.存储过程
create/call/drop
1 2 3 4 5 6 7 8 9 10 11 12
DROPPROCEDURE IF EXISTS get_payments; DELIMITER $$ -- 修改分隔符 USE `sql_invoicing`$$ CREATEPROCEDURE `get_payments`() BEGIN select*from payments; END$$ DELIMITER ; -- 还原分隔符 -- 或者右键create procedure
-- 输入参数 DROPPROCEDURE IF EXISTS get_clients_by_state; DELIMITER $$ CREATEPROCEDURE get_clients_by_state ( state char(2) ) begin select*from clients c where c.state=state; end$$ DELIMITER ;
-- 判断参数 DELIMITER $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `get_payments`( client_id INT, payment_method_id TINYINT) BEGIN SELECT*FROM payments p WHERE p.client_id=IFNULL(client_id,p.client_id) AND p.payment_method=IFNULL(payment_method_id,p.payment_id); -- 如果输入参数为null,则返回所有 END$$ DELIMITER ;
-- 输出错误代码 DELIMITER $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `make_payment`( invoice_id INT, payment_amount DECIMAL(9,2), payment_date DATE) BEGIN IF payment_amount<=0THEN SIGNAL SQLSTATE'22003'; SET MESAAGE_TEXT='Invalid Payment amount'; END IF; UPDATE invoices i SET i.payment_total=payment_amount, i.payment_date=payment_date WHERE invoice_id=invoice_id; END$$ DELIMITER ;
-- 参数前加out关键字,select into,使用输出参数读取数据
variable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-- User or session variable,客户端与数据库中断时,内存释放 Set@invoices_count=0; -- 存在内存中的变量 -- local varible (inside procedure) 存储过程结束时,内存释放 DELIMITER $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `get_risk_factor`() BEGIN DECLARE risk_factor DECIMAL(9,2) DEFAULT0; -- 声明变量 DECLARE invoices_total DECIMAL(9,2); DECLARE invoices_count INT; SELECTCOUNT(*),SUM(invoice_total) INTO invoices_count,invoices_total FROM invoices; SET risk_factor=invoices_total/invoices_count*5; SELECT risk_factor; END$$ DELIMITER ;
Function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
DELIMITER $$ CREATE DEFINER=`root`@`localhost` FUNCTION `get_risk_fact`(client_id INT) RETURNSINT READSSQL DATA BEGIN DECLARE risk_factor DECIMAL(9,2) DEFAULT0; DECLARE invoices_total DECIMAL(9,2); DECLARE invoices_count INT; SELECTCOUNT(*),SUM(invoice_total) INTO invoices_count,invoices_total FROM invoices i WHERE i.client_id=client_id; SET risk_factor=invoices_total/invoices_count*5; RETURN IFNULL(risk_factor,0); END$$ DELIMITER ;
调用function:select
1 2 3 4 5 6 7
SELECT client_id, name, get_risk_factor_for_client(client_id) as risk_factor FROM clients;
DROPFUNCTION IF EXISTS get_risk_factor_for_client;
10.触发和事件
Trigger
确保数据更新:满足条件时触发,并执行语句(insert/update/delete
-OLD
1 2 3 4 5 6 7 8 9 10 11 12 13 14
DELIMITER $$ DROPTRIGGER IF EXISTS payments_after_delete; CREATETRIGGER payments_after_delete AFTER DELETEON payments FOREACHROW BEGIN UPDATE invoices SET payment_total=payment_total-OLD.amount WHERE invoice_id=OLD.invoice_id; -- 删除触发器,old.columnname; new.columnname 新增行的某列数据 INSERTINTO payments_audit VALUES (OLD.client_id,OLD.date,OLD.amount,'Delete',now()); -- wrting logs END $$ DELIMITER ;
-- create SHOW VARIABLES LIKE'event%'; SETGLOBAL event_scheduler='ON';
delimiter $$ CREATE EVENT yearly_delete_stale_audit_rows ON SCHEDULE -- at '2019-05-01' EVERY1YEAR STARTS '2019-01-01' ENDS '2029-01-01' DO BEGIN DELETEFROM payments_audit WHERE action_date<NOW()-INTERVAL1YEAR; END$$ delimiter ;
-- view and drop SHOW EVENTS; SHOW EVENTS LIKE'yearly%'; -- 命名时写明时间间隔
-- Alter event ALTER EVENT yearly_delete_stale_audit_rows DISABLE; ALTER EVENT yearly_delete_stale_audit_rows ENABLE;
CREATE DATABASE IF NOTEXISTS sql_store2; USE sql_store2; CREATETABLE IF NOTEXISTS customers ( customer_id INTPRIMARY KEY AUTO_INCREMENT, first_name VARCHAR(15) NOTNULL, points INTNOTNULLDEFAULT0, email VARCHAR(255) NOTNULLUNIQUE );
-- alter ALTERTABLE customers ADD last_name VARCHAR(50) NOTNULL AFTER first_name, ADD city VARCHAR(50) NOTNULL, MODIFY COLUMN first_name VARCHAR(50) DEFAULT'', DROP points; DROPTABLE IF EXISTS orders; CREATETABLE orders ( order_id INTPRIMARY KEY, customer_id INTNOTNULL, FOREIGN KEY fk_orders_customers (customer_id) REFERENCES customers (customer_id) ON UPDATE CASCADE ONDELETENO ACTION );-- primary key 默认not null
-- character set ALTERtable table1 CHARACTERSET latin1;
14.Indexing for high performance
indexes are usually stored as binary tree
creating indexes
1 2 3 4 5
CREATE INDEX idx_state ON customers (state); DROP INDEX idx_state ON customers;
SHOW INDEXES IN orders; EXPLAIN SELECT customer_id FROM customers WHERE state='CA'AND points>1000; -- rows scanned
string
前缀索引
1 2 3 4 5 6 7
-- prefix indexes for string CREATE INDEX idx_lastname ON customers (last_name(5)); -- 选择索引长度 SELECTCOUNT(DISTINCTLEFT(last_name,1)), COUNT(DISTINCTLEFT(last_name,5)), COUNT(DISTINCTLEFT(last_name,10)) FROM customers;
为全文建立索引
1 2 3 4 5 6 7
-- full-text indexes /long articles CREATE FULLTEXT INDEX idx_title_body ON posts (title,body);
SELECT*, MATCH (title, body) AGAINST ('react redux') FROM posts WHEREMATCH (title, body) AGAINST ('react redux'); -- MATCH (title, body): relevance score
联合索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14
-- composite index CREATE INDEX idx_state_points ON customers (state,points); -- order of column: frequently-used column, high cardinality, take your queries into account
-- 指定index EXPLAIN SELECT customer_id FROM customers USE INDEX (ID_STATE_POINTS) WHERE state='CA'AND points>1000; -- rows scanned
-- when indexes are ignored -- select or/ 建立index, then union CREATE INDEX idx_points ON customers (points); explain select customer_id from customers where points+10>2010; -- 扫描索引的所有记录 full index scan explain select customer_id from customers where points>2000; -- 单独写索引
using indexes for sorting
建立组合索引(a,b),排序
使用索引排序:
a
a,b
a DESC, b DESC
a,c,b —全表扫描
b —全表扫描
While creating secondary index,sql will include primary key
如果只选择索引内的列(covering index),则索引扫描;否则全表扫描
index maintaining
duplicate indexes
redundant indexes: (A,B) (A) x
check existing indexes before creating
15. Users and privilege
Creating a user
1 2 3 4 5 6 7 8 9 10 11
-- creating users CREATEUSER john@localhost;-- host/domain name/ip address CREATEUSER john@'%.localhost'; -- sub domain CREATEUSER john IDENTIFIED BY'1234';-- can connect anywhere, password