約束條件與數據類型的寬度同樣,都是可選參數python
做用:用於保證數據的完整性和一致性mysql
主要分爲:nginx
PRIMARY KEY (PK) 標識該字段爲該表的主鍵,能夠惟一的標識記錄
FOREIGN KEY (FK) 標識該字段爲該表的外鍵
NOT NULL 標識該字段不能爲空
UNIQUE KEY (UK) 標識該字段的值是惟一的
AUTO_INCREMENT 標識該字段的值自動增加(整數類型,並且爲主鍵)
DEFAULT 爲該字段設置默認值
UNSIGNED 無符號
ZEROFILL 使用0填充
說明:程序員
1. 是否容許爲空,默認NULL,可設置NOT NULL,字段不容許爲空,必須賦值sql
2. 字段是否有默認值,缺省的默認值是NULL,若是插入記錄時不給字段賦值,此字段使用默認值數據庫
sex enum('male','female') not null default 'male'網絡
age int unsigned NOT NULL default 20 必須爲正值(無符號) 不容許爲空 默認是20函數
3. 是不是keyoop
主鍵 primary keypost
外鍵 foreign key
索引 (index,unique...)
not null - 不可空
null - 可空
默認值,建立列時能夠指定默認值,當插入數據時若是未主動設置,則自動添加默認值
create table t4(x char(4) not null); create table user(id int,name char(16),sex enum('male','female') not null default 'male');
約束字段不爲空而且惟一
依據:主鍵是innodb的表組織數據的依據
注意:一張表中必須有且只能有一個主鍵
一個表中能夠:
單列作主鍵
多列作主鍵(複合主鍵)
create table t5( id int primary key auto_increment, name char(20) ); insert into t5(name) values('egon'),('alex'); select name from t5 where id = 1000;
瞭解:聯合主鍵
create table t6( id int, name char(20), primary key(id,name) );
create table service( id int primary key auto_increment, name char(20) unique );
not null+unique:會在沒有指定主鍵的狀況下被識別爲主鍵
create table service1( id int, name char(20) not null unique ); create table service2( id int primary key auto_increment, name char(20) unique, ip char(15), port int, unique(ip,port) ); insert into service2(name,ip,port) values ('nginx','192.168.1.10',80), ('haproxy','192.168.1.10',8080), ('mysql','192.168.1.10',3306); insert into service2(name,ip,port) values ('nginx1','192.168.1.10',80);
約束字段爲自動增加,被約束的字段必須同時被key約束
show variables like '%auto%';
# 結果
auto_increment_increment | 1 # 步長
auto_increment_offset | 1 # 起始位置
#強調:起始位置的值必須<=步長
基於全局級別的
set global auto_increment_increment=5; set global auto_increment_offset=3;
若是auto_increment_offset的值大於auto_increment_increment的值,則auto_increment_offset的值會被忽略
create table t7(id int unique auto_increment); insert into t7 values(); set global auto_increment_increment=1; set global auto_increment_offset=1;
建立表:
一、先建立被關聯的表:部門表
#強調:被關聯的字段必須惟一
create table dep( id int primary key auto_increment, name char(10) );
二、再建立關聯表:員工表
create table emp( id int primary key auto_increment, name char(16), age int, dep_id int, foreign key(dep_id) references dep(id) on delete cascade on update cascade );
插入記錄時:
#一、先往被關聯表插入:部門表
insert into dep(name) values ('外交部'), ('技術哈哈部'), ('銷售部');
#二、再往關聯表插入:員工表
insert into emp(name,age,dep_id) values ('egon',19,1), ('alex',74,2), ('wxx',38,2), ('yxx',22,3);
#錯誤操做
insert into emp(name,age,dep_id) values ('lhf',29,4);
delete from dep where id=2; delete from emp where dep_id=2;
分析步驟:
#一、先站在左表的角度去找
是否左表的多條記錄能夠對應右表的一條記錄,若是是,則證實左表的一個字段foreign key 右表一個字段(一般是id)
#二、再站在右表的角度去找
是否右表的多條記錄能夠對應左表的一條記錄,若是是,則證實右表的一個字段foreign key 左表一個字段(一般是id)
三張表:出版社,做者信息,書
書 出版社
一個出版社能夠出版多本書
若是隻有步驟1成立,則是左表多對一右表
若是隻有步驟2成立,則是右表多對一左表
三張表:出版社,做者信息,書
多對多:一個做者能夠寫多本書,一本書也能夠有多個做者,雙向的一對多,即多對多
做者 書
若是步驟1和2同時成立,則證實這兩張表時一個雙向的多對一,即多對多,須要定義一個這兩張表的關係表來專門存放兩者的關係
客戶表 學生表
若是1和2都不成立,而是左表的一條記錄惟一對應右表的一條記錄,反之亦然。這種狀況很簡單,就是在左表foreign key右表的基礎上,將左表的外鍵字段設置成unique便可
select distinct 字段1, 字段2,... from 表名 where 約束條件 group by 分組條件 having 過濾條件 order by 排序字段 limit n;
1.找到表:from
2.拿着where指定的約束條件,去文件/表中取出一條條記錄
3.將取出的一條條記錄進行分組group by,若是沒有group by,則總體做爲一組
4.將分組的結果進行having過濾
5.執行select
6.去重
7.將結果按條件排序:order by
8.限制結果的顯示條數
select * from emp; select id,name from emp;
去除重複
select distinct post from emp;
四則運算
select name,salary*12 as annual_salary from emp; select name,salary*12 annual_salary from emp;
select concat('姓名: ',name) as new_name, concat('年薪: ',salary*12) as annual_salary from emp;
#CONCAT() 函數用於鏈接字符串
select concat(name,'_SB') as new_name from emp; select concat(name,'_SB') as new_name from emp where name != 'egon';
結合CASE語句:
select ( case when name = 'egon' then name when name = 'alex' then concat(name,'_BIGSB') else concat(name,'_SB') end ) as new_name from emp;
CONCAT_WS() 第一個參數爲分隔符
select concat(name,':',salary,':',sex) from emp; select concat_ws(':',name,salary,sex) from emp;
where字句中可使用:
1. 比較運算符:> < >= <= <> !=
2. between 80 and 100 值在10到20之間
3. in(80,90,100) 值是10或20或30
4. like 'egon%'
pattern能夠是%或_,
%表示任意多字符
_表示一個字符
5. 邏輯運算符:在多個條件直接可使用邏輯運算符 and or not
select id,name,age from emp where age > 20; select id,name,age from emp where age >= 20 and age <= 38; select id,name,age from emp where age between 20 and 38; select id,name,age from emp where age <= 20 or age >= 38; select id,name,age from emp where age not between 20 and 38; select * from emp where age=18 or age=28 or age=38; select * from emp where age in (18,28,38); select * from emp where age=18 or sex='male'; select * from emp where name like 'jin%'; select * from emp where name like '__';
分組依據應該找重複度比較高的字段
分組以後只能查到分組字段,或者聚合的結果
首先明確一點:分組發生在where以後,即分組是基於where以後獲得的記錄而進行的
分組指的是:將全部記錄按照某個相同字段進行歸類,好比針對員工信息表的職位分組,或者按照性別進行分組等
爲什麼要分組呢?
取每一個部門的最高工資
取每一個部門的員工數
取男人數和女人數
小竅門:‘每’這個字後面的字段,就是咱們分組的依據
大前提:
能夠按照任意字段分組,可是分組完畢後,好比group by post,只能查看post字段,若是想查看組內信息,須要藉助於聚合函數
因爲沒有設置ONLY_FULL_GROUP_BY,因而也能夠有結果,默認都是組內的第一條記錄,但其實這是沒有意義的
單獨使用GROUP BY關鍵字分組
SELECT post FROM employee GROUP BY post;
注意:咱們按照post字段分組,那麼select查詢的字段只能是post,想要獲取組內的其餘相關信息,須要藉助函數
GROUP BY關鍵字和GROUP_CONCAT()函數一塊兒使用
SELECT post,GROUP_CONCAT(name) FROM employee GROUP BY post;#按照崗位分組,並查看組內成員名 SELECT post,GROUP_CONCAT(name) as emp_members FROM employee GROUP BY post;
GROUP BY與聚合函數一塊兒使用
select post,count(id) as count from employee group by post;#按照崗位分組,並查看每一個組有多少人
強調:
若是咱們用unique的字段做爲分組的依據,則每一條記錄自成一組,這種分組沒有意義
多條記錄之間的某個字段值相同,該字段一般用來做爲分組的依據
set global sql_mode='strict_trans_tables,only_full_group_by'; #設置成功後,必定要退出,而後從新登陸方可生效 select post,max(salary) from emp group by post; select post,min(salary) from emp group by post; select post,sum(salary) from emp group by post; select post,avg(salary) from emp group by post; select post,count(id) from emp group by post; select post,group_concat(name) from emp group by post; select count(id) from emp; select count(id) from emp where max(salary) > 1;
HAVING與WHERE不同的地方在於!!!!!!
#!!!執行優先級從高到低:where > group by > having
#1. Where 發生在分組group by以前,於是Where中能夠有任意字段,可是絕對不能使用聚合函數。
#2. Having發生在分組group by以後,於是Having中可使用分組的字段,沒法直接取到其餘字段,可使用聚合函數
select post,avg(salary) from emp group by post having avg(salary) > 20000;
按單列排序
SELECT * FROM employee ORDER BY salary; SELECT * FROM employee ORDER BY salary ASC; SELECT * FROM employee ORDER BY salary DESC;
按多列排序:先按照age排序,若是年紀相同,則按照薪資排序
SELECT * from employee ORDER BY age, salary DESC; select * from emp order by age asc; # 默認升序,從小到大 select * from emp order by age desc; #從大到小 select * from emp order by age asc,id desc; select post,avg(salary) from emp group by post order by avg(salary);
SELECT * FROM employee ORDER BY salary DESC LIMIT 3; #默認初始位置爲0 SELECT * FROM employee ORDER BY salary DESC LIMIT 0,5; #從第0開始,即先查詢出第一條,而後包含這一條在內日後查5條 SELECT * FROM employee ORDER BY salary DESC LIMIT 5,5; #從第5開始,即先查詢出第6條,而後包含這一條在內日後查5條
練習:分頁顯示,每頁5條
select * from emp limit 3; select * from emp limit 0,5; select * from emp limit 5,5; select * from emp limit 10,5; select * from emp limit 15,5; def from(db,table): f=open(filepath,'r') return f def where(f,pattern): for line in f: if pattern: yield line def group(): pass def having(): pass def distinct(): pass def order(): pass def limit(): pass def select(): f=from(db,table) lines=where(f,'id > 1') group_res=group(lines) having_res=having(group_res) distinct_res=distinct(having_res) order_res=order(distinct_res) res=limit(order_res) print(res)
select * from emp where name regexp '^jin.*$'; SELECT * FROM employee WHERE name REGEXP 'on$'; SELECT * FROM employee WHERE name REGEXP 'm{2}'
小結:對字符串匹配的方式
WHERE name = 'egon'; WHERE name LIKE 'yua%'; WHERE name REGEXP 'on$';
select * from emp,dep; select * from emp,dep where emp.dep_id=dep.id;
select * from emp inner join dep on emp.dep_id=dep.id;
select * from emp left join dep on emp.dep_id=dep.id;
select * from emp right join dep on emp.dep_id=dep.id;
select * from emp left join dep on emp.dep_id=dep.id union select * from emp right join dep on emp.dep_id=dep.id;
#查詢平均年齡在25歲以上的部門名
select name from dep where id in (select dep_id from emp group by dep_id having avg(age) > 25);
#查看技術部員工姓名
select emp.name from emp inner join dep on emp.dep_id = dep.id where dep.name = '技術' ; select name from emp where dep_id =(select id from dep where name='技術');
#查看不足1人的部門名
select name from dep where id not in ( select dep_id from emp group by dep_id having count(id) >= 1); select name from dep where id not in ( select distinct dep_id from emp);
#查詢大於全部人平均年齡的員工名與年齡
select name,age from emp where age > ( select avg(age) from emp);
#查詢大於部門內平均年齡的員工名、年齡
select t1.* from emp as t1 inner join (select dep_id,avg(age) as avg_age from emp group by dep_id) as t2 on t1.dep_id = t2.dep_id where t1.age > t2.avg_age ;
# 統計每一個部門最新入職的員工名,入職日期
select t1.name,t1.hire_date from emp as t1 inner join (select post,max(hire_date) max_date from emp group by post) as t2 on t1.post = t2.post where t1.hire_date = t2.max_date ;
select name from emp where name='egon'; select name from emp where id > 1000; select name from emp where id > 1000 and id < 1005; select name from emp where id = 1000; select name from emp where salary = 20000/12; select * from emp;
#安裝
pip3 install pymysql
''' create table user( id int primary key auto_increment, username char(16), password char(20) ); insert into user(username,password) values ('egon','123'), ('alex','456'), ('wxx','456'); '''
import pymysql user=input('user>>: ').strip() pwd=input('password>>: ').strip() #一、建鏈接 conn=pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', db='db6' ) #二、拿遊標 cursor=conn.cursor() #三、提交sql # sql='select id from user where username="%s" and password="%s"' %(user,pwd) # print(sql) sql='select id from user where username=%s and password=%s' rows=cursor.execute(sql,(user,pwd)) if rows: print('登陸成功') else: print('用戶名或密碼錯誤') conn.commit() #四、回收資源 cursor.close() conn.close()
注意:符號--會註釋掉它以後的sql,正確的語法:--後至少有一個任意字符
根本原理:就根據程序的字符串拼接name='%s',咱們輸入一個xxx' -- haha,用咱們輸入的xxx加'在程序中拼接成一個判斷條件name='xxx' -- haha'
最後那一個空格,在一條sql語句中若是遇到select * from t1 where id > 3 -- and name='egon';則--以後的條件被註釋掉了
#一、sql注入之:用戶存在,繞過密碼
egon' -- 任意字符
#二、sql注入之:用戶不存在,繞過用戶與密碼
xxx' or 1=1 -- 任意字符
解決方法:
# 原來是咱們對sql進行字符串拼接
# sql="select * from userinfo where name='%s' and password='%s'" %(user,pwd) # print(sql) # res=cursor.execute(sql)
#改寫爲(execute幫咱們作字符串拼接,咱們無需且必定不能再爲%s加引號了)
sql="select * from userinfo where name=%s and password=%s" #!!!注意%s須要去掉引號,由於pymysql會自動爲咱們加上 res=cursor.execute(sql,[user,pwd]) #pymysql模塊自動幫咱們解決sql注入的問題,只要咱們按照pymysql的規矩來。
import pymysql #一、建鏈接 conn=pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', db='db6' ) #二、拿遊標 cursor=conn.cursor() #三、提交sql sql='insert into user(username,password) values(%s,%s)' # rows=cursor.execute(sql,('yxx','123')) # print(rows) rows=cursor.executemany(sql,[('yxx1','123'),('yxx2','123'),('yxx3','123')]) print(rows) conn.commit() #四、回收資源 cursor.close() conn.close()
import pymysql #一、建鏈接 conn=pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', db='db6' ) #二、拿遊標 cursor=conn.cursor(pymysql.cursors.DictCursor) #三、提交sql sql='select * from user' rows=cursor.execute(sql) # print(rows) # print(cursor.fetchall()) # print('===>',cursor.fetchall()) print(cursor.fetchone()) print(cursor.fetchone()) print(cursor.fetchmany(3)) cursor.scroll(0,mode='absolute') # 相對絕對位置移動 print(cursor.fetchone()) # cursor.scroll(3,mode='relative') # 相對當前位置移動 #四、回收資源 cursor.close() conn.close()
視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,併爲其命名】,用戶使用時只需使用【名稱】便可獲取結果集,能夠將該結果集當作表來使用。
使用視圖咱們能夠把查詢過程當中的臨時表摘出來,用視圖去實現,這樣之後再想操做該臨時表的數據時就無需重寫複雜的sql了,直接去視圖中查找便可,但視圖有明顯地效率問題,而且視圖是存放在數據庫中的,若是咱們程序中使用的sql過度依賴數據庫中的視圖,即強耦合,那就意味着擴展sql極爲不便,所以並不推薦使用
#語法:CREATE VIEW 視圖名稱 AS SQL語句
create view teacher_view as select tid from teacher where tname='李平老師';
#因而查詢李平老師教授的課程名的sql能夠改寫爲
mysql> select cname from course where teacher_id = (select tid from teacher_view); +--------+ | cname | +--------+ | 物理 | | 美術 | +--------+ 2 rows in set (0.00 sec)
#!!!注意注意注意:
#1. 使用視圖之後就無需每次都重寫子查詢的sql,可是這麼效率並不高,還不如咱們寫子查詢的效率高
#2. 並且有一個致命的問題:視圖是存放到數據庫裏的,若是咱們程序中的sql過度依賴於數據庫中存放的視圖,那麼意味着,一旦sql須要修改且涉及到視圖的部分,則必須去數據庫中進行修改,而一般在公司中數據庫有專門的DBA負責,你要想完成修改,必須付出大量的溝通成本DBA可能纔會幫你完成修改,極其地不方便
修改視圖,原始表也跟着改
select * from course; create view course_view as select * from course; #建立表course的視圖
咱們不該該修改視圖中的記錄,並且在涉及多個表的狀況下是根本沒法修改視圖中的記錄的
使用觸發器能夠定製用戶對錶進行【增、刪、改】操做時先後的行爲,注意:沒有查詢
# 插入前
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 插入後
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 刪除前
CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 刪除後
CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新前
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新後
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW BEGIN ... END '''
觸發器沒法由用戶直接調用,而知因爲對錶的【增/刪/改】操做被動引起的。
drop trigger tri_after_insert_cmd;
事務用於將某些操做的多個SQL做爲原子性操做,一旦有某一個出現錯誤,便可回滾到原來的狀態,從而保證數據庫數據完整性。
create table user( id int primary key auto_increment, name char(32), balance int ); insert into user(name,balance) values ('wsb',1000), ('egon',1000), ('ysb',1000);
#原子操做
start transaction; update user set balance=900 where name='wsb'; #買支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 update user set balance=1090 where name='ysb'; #賣家拿到90元 commit;
#出現異常,回滾到初始狀態
start transaction; update user set balance=900 where name='wsb'; #買支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 uppdate user set balance=1090 where name='ysb'; #賣家拿到90元,出現異常沒有拿到 rollback; commit; mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 1000 | | 2 | egon | 1000 | | 3 | ysb | 1000 | +----+------+---------+ 3 rows in set (0.00 sec)
存儲過程包含了一系列可執行的sql語句,存儲過程存放於MySQL中,經過調用它的名字能夠執行其內部的一堆sql
使用存儲過程的優勢:
#1. 用於替代程序寫的SQL語句,實現程序與sql解耦
#2. 基於網絡傳輸,傳別名的數據量小,而直接傳sql數據量大
使用存儲過程的缺點:
#1. 程序員擴展功能不方便
#方式一:
MySQL:存儲過程
程序:調用存儲過程
#方式二:
MySQL:
程序:純SQL語句
#方式三:
MySQL:
程序:類和對象,即ORM(本質仍是純SQL語句)
delimiter // create procedure p1() BEGIN select user,host from mysql.user; END // delimiter ;
#在mysql中調用
call p1()
#在python中基於pymysql調用
cursor.callproc('p1') print(cursor.fetchall())
對於存儲過程,能夠接收參數,其參數有三類:
#in 僅用於傳入參數用
#out 僅用於返回值用
#inout 既能夠傳入又能夠看成返回值
delimiter // create procedure p2( in n1 int, in n2 int, out n3 int ) BEGIN select * from emp where id > n1 and id < n2; set n3=1; END // delimiter ;
-- 無參數
call proc_name()
-- 有參數,全in
call proc_name(1,2)
-- 有參數,有in,out,inout
set @t1=0; set @t2=3; call proc_name(1,2,@t1,@t2)
在python中基於pymysql執行存儲過程
import pymysql #一、建鏈接 conn=pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', db='db5' ) #二、拿遊標 cursor=conn.cursor(pymysql.cursors.DictCursor) #三、提交sql # cursor.callproc('p1') # print(cursor.fetchall()) cursor.callproc('p2',(3,5,0)) #@_p2_0=3,@_p2_1=5,@_p2_2=0 print(cursor.fetchall()) cursor.execute('select @_p2_2') print(cursor.fetchone()) #四、回收資源 cursor.close() conn.close()
drop procedure proc_name;
MySQL中提供了許多內置函數
mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); -> 'Sunday October 2009' mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); -> '22:23:00' mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00', -> '%D %y %a %d %m %b %j'); -> '4th 00 Thu 04 10 Oct 277' mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', -> '%H %k %I %r %T %S %w'); -> '22 22 10 10:23:00 PM 22:23:00 00 6' mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); -> '1998 52' mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); -> '00'
準備表和記錄
CREATE TABLE blog ( id INT PRIMARY KEY auto_increment, NAME CHAR (32), sub_time datetime ); INSERT INTO blog (NAME, sub_time) VALUES ('第1篇','2015-03-01 11:31:21'), ('第2篇','2015-03-11 16:31:21'), ('第3篇','2016-07-01 10:21:31'), ('第4篇','2016-07-22 09:23:21'), ('第5篇','2016-07-23 10:11:11'), ('第6篇','2016-07-25 11:21:31'), ('第7篇','2017-03-01 15:33:21'), ('第8篇','2017-03-01 17:32:21'), ('第9篇','2017-03-01 18:31:21');
#3. 提取sub_time字段的值,按照格式後的結果即"年月"來分組
SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m');
#結果
+-------------------------------+----------+ | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) | +-------------------------------+----------+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +-------------------------------+----------+ 3 rows in set (0.00 sec)
#!!!注意!!!
#函數中不要寫sql語句(不然會報錯),函數僅僅只是一個功能,是一個在sql中被應用的功能
#若要想在begin...end...中寫sql,請用存儲過程
delimiter // create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END // delimiter ;
# 獲取返回值
select UPPER('egon') into @res; SELECT @res;
# 在查詢中使用
select f1(11,nid) ,name from tb2;
drop function func_name;
delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSEIF i = 2 THEN SELECT 2; ELSE SELECT 7; END IF; END // delimiter ;
delimiter // CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ; END // delimiter ;
delimiter // CREATE PROCEDURE proc_repeat () BEGIN DECLARE i INT ; SET i = 0 ; repeat select i; set i = i + 1; until i >= 5 end repeat; END // delimiter ;
BEGIN declare i int default 0; loop_label: loop set i=i+1; if i<8 then iterate loop_label; end if; if i>=10 then leave loop_label; end if; select i; end loop loop_label; END