SQL进阶学习
视图
我们有时候需要经常性的需要一些查询结果,快速它的查询过程极为繁琐,这种情况下可以采用视图将结果进行保存。再下一次查询的时候只需要查询视图即可。
视图不存储数据,他只是数据的搬运工。因此数据表中数据的更新也会使视图中的数据更新。
视图创建
我们来将每一个顾客的收支情况存入视图。
CREATE VIEW clients_balance AS
SELECT c.client_id,c.name,SUM(invoice_total-payment_total) AS balance
FROM clients c
JOIN invoices i USING (client_id)
GROUP BY client_id,NAME;
视图查询
属性查询
DESCRIBE clients_balance;
DESC clients_balance;
数据查询
select * from clients_balance;
视图修改
我们可以通过修改视图来重新构建/获取视图,OR REPLACE 关键词可以做到。
CREATE OR REPLACE VIEW clients_balance AS
SELECT c.client_id,c.name,SUM(invoice_total-payment_total) AS balance
FROM clients c
JOIN invoices i USING (client_id)
GROUP BY client_id,NAME;
视图删除
DROP VIEW clients_balance;
DROP VIEW IF EXISTS clients_balance;
可更新视图
有时候我们没有对数据表的数据进行增删改的操作权限,但是我们又需要有这样的业务。那么我们可以通过创建一个可更新视图来完成这样的操作。
可更新视图有以下几个特点:
- 视图中不含有
DICTINCT 函数。 - 视图中没有聚合函数(
MAX/MIN/SUM )等等。 - 视图中不含有
GROUP BY 等分组。 - 视图中不含有
UNION 联合查询。
满足上述条件我们就可以对他进行增删改,结构和在数据表中的操作差不多。
注意在视图中的更新也会在数据表中更新,也就是说视图中删除了,原表中也将会删除数据。
INSERT INTO clients_view VALUE(6,'wang')
update clients_view set name='wnag'
where id=1;
DELETE FROM clients_view
WHERE NAME='zhang'
WITH CHECk OPTION
我们在更新视图的查询过程,可能会减少一些行,有时候我们并不想要减少这些行,可以通过WITH CHECR OPTION 关键词来获取这些行。
CREATE OR REPLACE VIEW clients_balance AS
SELECT invoice_id,
invoice_total,
payment_total,
invoice_total-payment_total AS balance
FROM invoices
WHERE (invoice_total-payment_total)>0
WITH CHECK OPTION;
然后我们在视图中寻找一行,更新该行的payment_total 使balance 值为0,看看该行会不会消失。
UPDATE clients_balance
SET payment_total = invoice_total
WHERE invoice_id=3
此时会报错,提示检查视图失败,通过这种方式我们可以避免视图中的数据丢失。
视图的优点
- 简化查询。
- 减少数据表改变带来的影响。
- 限制用户对数据的访问。
存储过程
我们知道在开发过程中,我们不仅仅需要一门语言,SQL仅仅是数据存储中使用的一门语言。我们不想在其它地方使用它,比如JAVA的代码中,那会使我们很难部署并且调试我们的代码。
我觉得存储过程就是一个面向对象的设计,把数据的管理通过存储过程来实现,可以增强数据的安全性。
创建存储过程
CREATE PROCEDURE get_client()
BEGIN
SELECT * FROM clients;
END
在除了MySQL 数据库以外的地方这样写好像没有什么问题,但是MySQL 中需要更换写法。因为SQL的语法需要在select 以后加一个分号,但是我们的语句是到END 才结束。所以需要改变分隔符。
DEFINER
定义该存储过程是是写的,如果没有写,默认为创建者。
DELIMITER $$
CREATE DEFINER `root`@`localhost` PROCEDURE get_client()
BEGIN
SELECT * FROM clients;
END$$
DELIMITER ;
DELIMITER
DELIMITER 可以更改分隔符。
DELIMITER $$
CREATE PROCEDURE get_client()
BEGIN
SELECT * FROM clients;
END$$
DELIMITER ;
SQL中调用存储过程
CALL get_client();
删除数据库
DROP PROCEDURE get_clients;
DROP PROCEDURE IF EXISTS get_clients;
带参数的存储过程
为了保证起的名字不一样,我喜欢用给数据表起别名的方式来进行区分,多个参数用, 逗号分隔即可。
也有一些大佬喜欢更改参数名称,请自便。
DELIMITER $$
CREATE PROCEDURE get_client_by_id(client_id INT(11))
BEGIN
SELECT * FROM clients c
WHERE c.client_id=client_id;
END$$
DELIMITER ;
CALL get_client_by_id(1);
给参数默认值的存储过程
其实就是IF 语句的使用
DROP PROCEDURE IF EXISTS get_client_by_id;
DELIMITER $$
CREATE PROCEDURE get_client_by_id(client_id INT(11))
BEGIN
IF client_id IS NULL THEN
SET client_id=1;
END IF;
SELECT * FROM clients c
WHERE c.client_id=client_id;
END$$
DELIMITER ;
CALL get_client_by_id(NULL);
当然我们也可以使用IFNULL ,注意SQL server 可能使用的是ISNULL 。
DROP PROCEDURE IF EXISTS get_client_by_id;
DELIMITER $$
CREATE PROCEDURE get_client_by_id(client_id INT(11))
BEGIN
SELECT * FROM clients c
WHERE c.client_id=IFNULL(client_id,c.client_id);
END$$
DELIMITER ;
CALL get_client_by_id(NULL);
参数校验
SIGNAL SQLSTATE 去获取错误编码,SET MESSAGE_TEXT 写入错误信息。
关于错误编码的事情网上搜SQL STATE 就有很多,我就不列举了。
下列例子对编号小于等于0的进行了验证:
DROP PROCEDURE IF EXISTS get_client_by_id;
DELIMITER $$
CREATE PROCEDURE get_client_by_id(client_id INT(11))
BEGIN
IF client_id<=0 THEN
SIGNAL SQLSTATE '22003'
SET MESSAGE_TEXT = 'Invalid payment amount';
END IF;
SELECT * FROM clients c
WHERE c.client_id=client_id;
END$$
DELIMITER ;
CALL get_client_by_id(-1);
存储过程输出
OUT 关键字。
DROP PROCEDURE IF EXISTS get_client_by_id;
DELIMITER $$
CREATE PROCEDURE get_client_by_id(
client_id INT,
OUT p_name VARCHAR(50))
BEGIN
SELECT c.name
INTO p_name
FROM clients c
WHERE c.client_id=IFNULL(client_id,c.client_id);
END$$
DELIMITER ;
SET @NAME = '';
CALL get_client_by_id(NULL,@NAME);
SELECT @NAME;
关于存储过程的输出,我个人觉得其实没有那么好用,也不建议使用。
变量
直接赋值,相当于全局变量,作用域为当前整个事务。
SET @w=2;
SELECT @w;
DECLARE
主要用于存储过程中:
DECLARE var1 INT DEFAULT 0;
函数
DETERMINISTIC–保证输入数据一样时输出结果不变 READS SQL DATA --保证可以读取数据 MODIFIES SQL DATA–保证可以修改数据
DROP FUNCTION IF EXISTS get_name;
DELIMITER $$
CREATE FUNCTION get_name(client_id INT)
RETURNS VARCHAR(50)
BEGIN
DECLARE a VARCHAR(50);
SELECT c.name
INTO a FROM clients c WHERE c.client_id=client_id;
RETURN a;
END$$
DELIMITER ;
SELECT get_name(1);
触发器
创建触发器
DELIMITER $$
CREATE TRIGGER payments_after_delete
AFTER DELETE ON payments
FOR EACH ROW
BEGIN
UPDATE invoices
SET payment_total=payment_total -OLD.amount
WHERE invoice_id = old.invoice_id;
END $$
DELIMITER ;
DELETE FROM payments WHERE payment_id=8;
查询触发器
SHOW TRIGGERS;
SHOW TRIGGERS LIKE 'payments%';
删除触发器
DROP TRIGGER IF EXISTS payments_after_delete;
事件
先在sql 变量表中查询所有的变量信息,找到并打开事件系统。
SHOW VARIABLES LIKE 'event%';
SET GLOBAL event_scheduler = ON;
创建事件
DELIMITER $$
CREATE event yearly_delete_stale_audit_rows
ON SCHEDULE
at ''
EVERY 1 YEAR STARTS '2020-01-01' ENDS '2021-01-02'
DO BEGIN
DELETE FROM payments_audit
WHERE action_date<NOW()-INTERVAL 1 YEAR;
END $$
DELIMITER ;
查询事件
SHOW EVENTS;
SHOW EVENTS IF EXISTS yearly_delete_stale_audit_rows;
修改事件
我们只需要把CREATE 改为ALTER 即可。
当然如果是开始或关闭该事件我们可以通过:
ALTER event yearly_delete_stale_audit_rows DISABLE;
删除事件
DROP EVENTS yearly_delete_stale_audit_rows;
DROP EVENTS IF EXISTS yearly_delete_stale_audit_rows
事务
事务的属性
事务的属性可以用ACID 来概括。也就是原子性atomicity ,一致性consistency ,隔离性isolation ,持久性durability 。换而言之就是保证该事务能够不受外界干扰的执行,并且结果可以持久保持的性质。
事务的创建
MySQL自带的有一个事务的情况,当我们的增删改操作在使用的时候,会自动加入事务,这样一行数据的更新即为一个事务。
SHOW VARIABLES LIKE 'autocommit%';
查到后发现默认开启状态。
那么我们有时候需要对于多行进行操作并且保证在一个事务里边:
START TRANSACTION; 事务的开始,COMMIT 事务的提交。
USE sql_store;
START TRANSACTION;
INSERT INTO orders(customer_id,order_date,STATUS)
VALUE(1,'2021-01-01',1);
INSERT INTO orders_item
VALUE(LAST_INSERT_ID(),1,1,1);
COMMIT;
有时候我们会人为的想取消该事务并且不让该事务对数据造成影响使用回滚ROLLBACK ,去关闭事务并撤销所有更改。
|