我们有时候需要经常性的需要一些查询结果,快速它的查询过程极为繁琐,这种情况下可以采用视图将结果进行保存。再下一次查询的时候只需要查询视图即可。
视图不存储数据,他只是数据的搬运工。因此数据表中数据的更新也会使视图中的数据更新。
我们来将每一个顾客的收支情况存入视图。
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 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
才结束。所以需要改变分隔符。
定义该存储过程是是写的,如果没有写,默认为创建者。
DELIMITER $$
CREATE DEFINER `root`@`localhost` PROCEDURE get_client()
BEGIN
SELECT * FROM clients;
END$$
DELIMITER ;
DELIMITER
可以更改分隔符。
DELIMITER $$
CREATE PROCEDURE get_client()
BEGIN
SELECT * FROM clients;
END$$
DELIMITER ;
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 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
即可。
当然如果是开始或关闭该事件我们可以通过:
--enable开启事件
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
,去关闭事务并撤销所有更改。