数据库数据定义语句
建表
1. CREATE TABLE <表名>
(<列名> <数据类型>[ <列级完整性约束条件> ]
[,<列名> <数据类型>[ <列级完整性约束条件>] ]
• …
[,<表级完整性约束条件> ] );
...
ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2. 列级完整性约束条件
* PRIMARY KEY
* UNIQUE
* REFERENCES Course(Cno)
3. 表级完整性约束条件
CONSTRAINT `约束名` +
PRIMARY KEY (Sno,Cno),
FOREIGN KEY (Sno) REFERENCES Student(Sno),
CHECK(Grade<=100 AND Grade>=0)
...
+
ON DELETE RESTRICT ON UPDATE RESTRICT/cascade,
4. 模式与基本表
每一个基本表都属于某一个模式,一个模式包含多个基本表
定义基本表所属模式:
方法一:在表名中明显地给出模式名
CREATE TABLE S-T.Student(......); /*模式名为S-T*/
方法二:在创建模式语句中同时创建表
CREATE SCHEMA S-T AUTHORIZATION WANG
CREATE TABLE Student(......);
5. 修改删除基本表
ALTER ALTER TABLE <表名>
[ ADD <新列名> <数据类型> [ 完整性约束 ] ]
[ DROP [COLUMN]<列名> [CASCADE | RESTRICT ] ]
[ ADD <表级完整性约束名> ]
[ DROP CONSTRAINT <完整性约束名> [CASCADE | RESTRICT ] ]
[ ALTER COLUMN<列名> <数据类型> ];
DROP TABLE <表名>[RESTRICT| CASCADE];
选择
SELECT [ALL | DISTINCT] <目标列表达式>[,<目标列表达式>] …
FROM <表名或视图名>[, <表名或视图名> ] …
[ WHERE <条件表达式> ]
[ GROUP BY <列名1> [ HAVING <条件表达式> ] ]
[ ORDER BY <列名2> [ ASC | DESC ] ];
...
LIMIT 10;
select name as rename
from table1 as t11,table1 as t12,table2 as t2
连接方式
常用
SELECT * FORM TABLE_A A INNER JOIN TABLE_B B ON A.KEY = B.KEY
SELECT * FORMTABLE_A A LEFT JOIN TABLE_B B ON A.KEY = B.KEY
SELECT * FROM TABLE_A A FULL OUTER JOIN TABLE_B B ON A.KEY = B.KEY
用户变量
SELECT train.TID
FROM train,(SELECT @tmpday1:=FDay FROM diagnoserecord WHERE diagnoserecord.PCardID=@pcardid1) as tablerename(columnrename)
WHERE train.SDate BETWEEN DATE_SUB(@tmpday1 ,INTERVAL 100 day)and '@tmpday1');
where +条件表达式
查询条件 谓 词
比 较 =,>,<,>=,<=,!=,<>,!>,!<;NOT
确定范围 BETWEEN AND,NOT BETWEEN AND
确定集合 IN,NOT IN
字符匹配 LIKE,NOT LIKE
空 值 IS NULL,IS NOT NULL 注意is null 不能写 =null
多重条件(逻辑运算) AND,OR,NOT
ANY或ALL谓词的子查询
EXISTS/NOT EXISTS
where exits(..)
字符匹配
[NOT] LIKE ‘<匹配串>’[ESCAPE‘ <换码字符>’]
百分号% 代表任意长度的字符串
下划线__ 代表任意一个字符
ESCAPE 是将百分号% 或下划线__转回其本意
mysql查询以字符串开头的数据可以有:
0、 Cname LIKE ‘DB\_%i_ _ ’ ESCAPE '\' ;
1、select * from a where a.name like 'H%'
2、select * from a where left(a.name, 1)='H';
mysql查询不以某个字符串开头:
select * from a where left(a.name, 1)<>'H';
聚集函数
集合并交查
select ...
union
select...
– 并操作UNION
– 交操作INTERSECT
– 差操作EXCEPT
增删改
插入数据
INSERT INTO <表名> [(<属性列1>[,<属性列2>]…)]
VALUES (<常量1>[,<常量2>]….),(多列逗号分隔)
| SELECT 子查询;
修改数据
UPDATE <表名>
SET <列名>=<表达式>[, <列名>=<表达式>]…
[WHERE <条件>];
删除数据
DELETE
FROM <表名>
[WHERE <条件>];
mysql常用函数
字符串函数
CANCAT(S1,S2,…Sn) 连接 S1,S2,…Sn 为一个字符串
INSERT(str,x,y,instr) 将字符串 str 从第 x 位置开始,y 个字符长的子串替换为字符串 instr
LOWER(str) 将字符串 str 中所有字符变为小写
UPPER(str) 将字符串 str 中所有字符变为大写
LEFT(str ,x) 返回字符串 str 最左边的 x 个字符
RIGHT(str,x) 返回字符串 str 最右边的 x 个字符
LPAD(str,n ,pad) 用字符串 pad 对 str 最左边进行填充,直到长度为 n 个字符长度
RPAD(str,n,pad) 用字符串 pad 对 str 最右边进行填充,直到长度为 n 个字符长度
LTRIM(str) 去掉字符串 str 左侧的空格
RTRIM(str) 去掉字符串 str 行尾的空格
REPEAT(str,x) 返回 str 重复 x 次的结果
REPLACE(str,a,b) 用字符串 b 替换字符串 str 中所有出现的字符串 a
STRCMP(s1,s2) 比较字符串 s1 和 s2
TRIM(str) 去掉字符串行尾和行头的空格
SUBSTRING(str,x,y) 返回从字符串 str x 位置起 y 个字符长度的字串
数值函数
ABS(x) 返回 x 的绝对值
CEIL(x) 返回大于 x 的最大整数值
FLOOR(x) 返回小于 x 的最大整数值
MOD(x,y) 返回 x/y 的模
RAND() 返回 0 到 1 内的随机值
ROUND(x,y) 返回参数 x 的四舍五入的有 y 位小数的值
TRUNCATE(x,y) 返回数字 x 截断为 y 位小数的结果
时间日期函数
CURDATE() 返回当前日期
CURTIME() 返回当前时间
NOW() 返回当前的日期和时间
UNIX_TIMESTAMP(date) 返回日期 date 的 UNIX 时间戳
FROM_UNIXTIME 返回 UNIX 时间戳的日期值
WEEK(date) 返回日期 date 为一年中的第几周
YEAR(date) 返回日期 date 的年份
HOUR(time) 返回 time 的小时值
MINUTE(time) 返回 time 的分钟值
MONTHNAME(date) 返回 date 的月份名
DATE_FORMAT(date,fmt) 返回按字符串 fmt 格式化日期 date 值
DATE_ADD(date,INTERVAL expr type) 返回一个日期或时间值加上一个时间间隔的时间值
DATEDIFF(expr,expr2) 返回起始时间 expr 和结束时间 expr2 之间的天数
常用dateformate 中的格式
'%Y,%m,%d'
%S,%s 两位数字形式的秒(00,01,...,59)
%i 两位数字形式的分(00,01,...,59)
%H 两位数字形式的小时,24 小时(00,01,...,23)
%h,%I 两位数字形式的小时,12 小时(01,02,...,12)
%k 数字形式的小时,24 小时(0,1,...,23)
%l 数字形式的小时,12 小时(1,2,...,12)
%T 24 小时的时间形式(hh:mm:ss)
%r 12 小时的时间形式(hh:mm:ssAM 或 hh:mm:ssPM)
%p AM 或 PM
%W 一周中每一天的名称(Sunday,Monday,...,Saturday)
%a 一周中每一天名称的缩写(Sun,Mon,...,Sat)
%d 两位数字表示月中的天数(00,01,...,31)
%e 数字形式表示月中的天数(1,2,...,31)
%D 英文后缀表示月中的天数(1st,2nd,3rd,...)
%w 以数字形式表示周中的天数(0=Sunday,1=Monday,...,6=Saturday)
%j 以 3 位数字表示年中的天数(001,002,...,366)
%U 周(0,1,52),其中 Sunday 为周中的第一天
%u 周(0,1,52),其中 Monday 为周中的第一天
%M 月名(January,February,...,December)
90
%b 缩写的月名(January,February,...,December)
%m 两位数字表示的月份(01,02,...,12)
%c 数字表示的月份(1,2,...,12)
%Y 4 位数字表示的年份
%y 两位数字表示的年份
%% 直接值“%”
dateadd datediff中
HOUR 小时 hh
MINUTE 分 mm
SECOND 秒 ss
YEAR 年 YY
MONTH 月 MM
DAY 日 DD
YEAR_MONTH 年和月 YY-MM
DAY_HOUR 日和小时 DD hh
DAY_MINUTE 日和分钟 DD hh:mm
DAY_ SECOND 日和秒 DD hh:mm:ss
HOUR_MINUTE 小时和分 hh:mm
HOUR_SECOND 小时和秒 hh:ss
MINUTE_SECOND 分钟和秒 mm:ss
流程函数
IF(value,t f) 如果 value 是真,返回 t;否则返回 f
IFNULL(value1,value2) 如果 value1 不为空返回 value1,否则返回 value2
CASE WHEN [value1]
THEN[result1]…ELSE[default]END
如果 value1 是真,返回 result1,否则返回 default
CASE [expr] WHEN [value1]
THEN[result1]…ELSE[default]END
如果 expr 等于 value1,返回 result1,否则返回 default
其它函数
DATABASE() 返回当前数据库名
VERSION() 返回当前数据库版本
USER() 返回当前登录用户名
INET_ATON(IP) 返回 IP 地址的数字表示
INET_NTOA(num) 返回数字代表的 IP 地址
PASSWORD(str) 返回字符串 str 的加密版本
MD5() 返回字符串 str 的 MD5 值
视图
CREATE VIEW <视图名> [(<列名> [,<列名>]…)]
AS <子查询>
[WITH CHECK OPTION];
可选check option表示创建后 增改仍需满足where限制条件
可多表,可带avg 等表达式
DROP VIEW <视图名> [CASCADE];使用CASCADE级联删除语句,把
该视图和由它导出的所有视图一起删除
增删改同表操作
视图的更新最终要转换为对基本表的更新。
为防止用户通过视图更新,对不属于视图范围内的基本表数据进行更新操作,
在定义视图时加上WITH CHECK OPTION操作
SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern']查看视图
安全性
用户表示 存取控制 视图 审计 加密 这里主要存取控制中自主存取
GRANT <权限>[,<权限>]...
[ON <对象类型> <对象名>]
TO <用户>[,<用户>]...
[WITH GRANT OPTION];
GRANT UPDATE(Sno), SELECT
ON TABLE Student TO U4,u2;
REVOKE <权限>[,<权限>]...
[ON <对象类型> <对象名>]
FROM <用户>[,<用户>]...[CASCADE | RESTRICT];
也可以先给角色权限,再把角色给用户或角色
CREATE ROLE CREATE ROLE <角色名> <角色名>
GRANT <权限>[,<权限>]…
ON <对象类型>对象名
TO <角色>[,<角色>]…
REVOKE <权限>[,<权限>]…
ON <对象类型> <对象名>
FROM <角色>[,<角色>]…
GRANT <角色1>[,<角色2>]…
TO <角色3>[,<用户1>]…
[WITH ADMIN OPTION]
FLUSH PRIVILEGES; 刷新权限
强制存取控制主要 向上写 向下读
触发器
eg:
DROP TRIGGER IF EXISTS TrigerBuy;
CREATE TRIGGER TrigerBuy
AFTER UPDATE ON buy
FOR EACH ROW
BEGIN
IF old.bstatus=0 AND new.bstatus=1 -- 审核通过
THEN
set @lacknum=(SELECT IFNULL(lnum,0) FROM lack WHERE lno=new.bgno); -- 查缺货数量,不缺为0
if @lacknum=0
then UPDATE inventory set inum=inum+new.bnum WHERE ino=new.bgno; -- 不缺货 增库存
else set @changenum=new.bnum-@lacknum;
if @changenum>=0 -- 缺货 新货凑够发货 更新库存,删缺货表记录,缺货单状态改为1完成
then
UPDATE inventory set inum=@changenum WHERE ino=new.bgno;
UPDATE sell set sstatus=1 WHERE sgno=new.bgno and sstatus=2;
DELETE FROM lack WHERE lno=new.bgno;
else UPDATE lack set lnum=-@changenum WHERE lno=new.bgno; -- 缺货还不够发货,增库存 减缺货量
UPDATE inventory set inum=inum+new.bnum WHERE ino=new.bgno;
end if;
end if;
END IF;
END;
CREATE TRIGGER <触发器名>
{ BEFORE | AFTER } <触发事件>
ON <表名>
FOR EACH { ROW | STATEMENT } [WHEN <触发条件>]
<触发动作体>
DROP TRIGGER <触发器名> ON <表名>;
触发事件INSERT、DELETE、UPDATE mysql只能选一,且不能
for each statement(只执行一次,for each row 多表所有行满足就执行)
存储过程
优化
自然连接算法:
嵌套循环
排序合并 有序表,性能高
索引连接 参与连接的表的连接属性上存在索引
哈希连接 连接属性哈希分桶。内存限制了不能太大
代数优化:高效表达式
物理有优化:执行策略
基于代价优化:统计表信息,估计中间结果集大小,io代价。略
并发控制 锁 事务 故障恢复
A—— 原子性(Atomicity) C —— 一致性(Consistency)
I —— 隔离性(Isolation) D —— 持久性(Durability)
隔离级别
-
Lost Update
丢失更新:提交或者回滚一个事务,把其它事务已提交的更新的数据覆盖了。
If the two transactions want to change the same columns, the second transaction will overwrite the first one, therefore losing the first transaction update. -
READ UNCOMMITED(未提交读)
事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也成为脏读(Dirty Read)。很少使用READ UNCOMMITED.
某事务读取了其他未提交事务更新的数据,随后未提交事务采取了撤消
(Rollback)动作,导致该数据无效(“脏”数据) -
READ COMMITED (提交读,不可重复读)
大多数数据库系统的默认隔离级别都是(但是MYSQL不是)一个事务从开始到提交之前,所做的任何修改对其他事务都 是不可见的。这个级别有时候也叫做不可重复的(nonerepeatable read),一事务读取数据后,另一并发事务增删改,因为两次执行同样的查询,可能会得到不一样的结果。
-
REPEATABLE READ (可重复读)
解决了脏读问题。该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。但是可重复读隔离级别还是无法解决另一个幻读 (PhantomRead)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读 取该范围的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB 存储引擎通过多版并发控制(MVCC ,Multivesion Concurrency Control )解决了幻读问题。
-
SERIALIZABLE(可串行化) 可重复读是Mysql 默认的事务隔离级别。
SERIALIZABLE是最高的隔离级别。它通过强制事务串行,避免了前面说的幻读问题。简单的来说,SERIALIZABLE会在读的每一行数据上 都加上锁,所以可能导致大量的超时和锁征用问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况,才可考 虑用该级别。性能差
IsolationLevel 脏读(Dirty Read)不可重复读(Non Repeatable Read) 幻读(Phantom Read)
Read Uncommitted Yes Yes Yes
Read Committed - Yes Yes
Repeatable Read - - Yes
Serializable - - -
处理系统故障 ROLLBACK redo undo
检查点
锁
基本锁:排它锁(exclusive locks,简称X锁)和共享锁
(share locks,简称S锁)。
封锁粒度
关系数据库理论
结构设计
备份 引擎
java JDBC
最简单使用jdbc的示例步骤
-
加载驱动程序:Class.forName(“JDBCDriverClass的完全限定名”); //反射
-
建立连接:使用DriverManager类的静态方法getConnectionConnection connection=DriverManager.getConnection(databaseURL);
-
创建语句:Statement statement=connection.createStatement();
-
执行语句:statement.execute(“SQL语句”);//增删改操作ResultSet resultSet=statement.executeQuery(“SQL语句”);//查询操作
-
处理ResultSet:遍历ResultSet的每条记录while (resultSet.next()) resultSet.getString(n);
ResultSet获取列时,索引从1开始boolean next()
throws SQLException 将结果集游标往下移动一行,这时ResultSet引用指向的就是当前游标(遍历到的当前记录)。如果已经到达结果集最后,将会返回false,有可能抛异常,必须捕捉
X getX(String columnName或int columnIndex)
throws SQLException 获得当前记录某个字段的值,X是指具体的数据类型,视数据库表中字段的具体情况而定,并且每个数据类型都有两种重载方法,一种是以字段名称为参数,另一种是以字段索引为参数(字段索引从1开始),有可能抛异常,必须捕捉JDBC查询的返回值总是ResultSet,即使我们写这样的聚合查询SELECT SUM(score) FROM …,也需要按结果集读取:
ResultSet rs = …
if (rs.next()) {
double sum = rs.getDouble(1);
} -
关闭资源
jdbc中SQL语句拼接java变量
使用“+ +”
String sql = "select * from user where username='" + username + "' and password ='" + password + "' ";
sql注入举例
用户输入账户名 bob 密码 1234 然后查询自己信息
SELECT * FROM user WHERE login='bob' AND pass='1234'
如果用户输入 账户名 “bob' OR pass=” 密码 " OR pass='" 用以上拼接SQL语句后:
SELECT * FROM user WHERE login='bob' OR pass=' AND pass=' OR pass=''
成功不输入密码查询了。
插入自增表时获取自增主键
在创建PreparedStatement的时候,指定一个RETURN_GENERATED_KEYS标志位,表示JDBC驱动必须返回插入的自增主键,调用prepareStatement()时,第二个参数必须传入常量Statement.RETURN_GENERATED_KEYS,执行executeUpdate()方法后,必须调用getGeneratedKeys()获取一个ResultSet对象,这个对象包含了数据库自动生成的主键的值,读取该对象的每一行来获取自增主键的值。如果一次插入多条记录,那么这个ResultSet对象就会有多行返回值。如果插入时有多列自增,那么ResultSet对象的每一行都会对应多个自增值
eg:
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
try (PreparedStatement ps = conn.prepareStatement(
"INSERT INTO students (grade, name, gender) VALUES (?,?,?)",
Statement.RETURN_GENERATED_KEYS)) {
ps.setObject(1, 1); // grade
ps.setObject(2, "Bob"); // name
ps.setObject(3, "M"); // gender
int n = ps.executeUpdate(); // 1
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) {
long id = rs.getLong(1); // 注意:索引从1开始
}
}
}
}
PreparedStatement 参数化的SQL语句 重复执行高效率 避免SQL注入 +batch
PreparedStatement preparedStatement=connection.prepareStatement( "insert into Student (firstName, mi, lastName) values (?, ?, ?)");
在PreparedStatement对象中设置参数:
// setX(int parameterIndex,X value); 也可以不具体类型 serObject
parameterIndex是语句中参数的下标,从1开始preparedStatement.setString(1,"Jack”);
preparedStatement.setString(2,”A”);
preparedStatement.setString(3,"Ryan");
执行
preparedStatement.executeUpdate();executeUpdate返回执行操作后受影响的记录数
方法有execute、executeUpdate和executeQuery ecuteBatch
batch批量处理
try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)")) {
// 对同一个PreparedStatement反复设置参数并调用addBatch():
for (Student s : students) {
ps.setString(1, s.name);
ps.setBoolean(2, s.gender);
ps.setInt(3, s.grade);
ps.setInt(4, s.score);
ps.addBatch(); // 添加到batch
}
// 执行batch:
int[] ns = ps.executeBatch();
for (int n : ns) {
System.out.println(n + " inserted."); // batch中每个SQL执行的结果数量
}
}
CallableStatement 执行SQL存储过程
例子
public static void main(String[] args){
String url = "jdbc:mysql://localhost:3306/javacourse";
Connection conn = ConnectionFactory.create(url,"root","root");
if(conn != null){
try{
//Create a callable statement
CallableStatement stmt = conn.prepareCall(“{? = call studentFound(?,?)}”); //SQL转义语法,它通知驱动程序其中的代码应该被不同处理,调用存储过程studentFound
Scanner input = new Scanner(System.in);
System.out.print("Input first name:"); String firstName = input.nextLine();
System.out.print("Input last name:"); String lastName = input.nextLine();
stmt.setString(2, firstName); //设置第一个输入参数(第2个问号),注意=左边的?序号为1
stmt.setString(3, lastName); //设置第二个输入参数(第3个问号),注意参数序号
stmt.registerOutParameter(1, Types.INTEGER); //设置第一个参数(第一个?)的返回值类型
stmt.executeUpdate(); //执行存储过程
int matches = stmt.getInt(1); //获取返回值,因为这里的返回内容不是记录,是标量(scalar),理解为只有一个字段,序号为1
System.out.println(matches + " records matched");
conn.close();
} catch (SQLException e) {e.printStackTrace(); }
}
}
CallableStatement对象中设置参数:
IN参数:
callableStatement.setX(parameterIndex, ...); //设置输入值
OUT参数:
// registerOutParameter方法注册输出值的类型
callableStatement.registerOutParameter(parameterIndex, java.sql.Types.TINYINT);
callableStatement.executeQuery();
int x = callableStatement.getByte(parameterIndex); //获取OUT参数值
IN OUT参数:
callableStatement.setX(parameterIndex, ...); //设置输入值
// registerOutParameter方法注册输出值的类型
callableStatement.registerOutParameter(n3, java.sql.Types.TINYINT);
callableStatement.executeUpdate();
byte x = callableStatement.getByte(parameterIndex); //获取OUT参数值
获取元数据
Connection对象的getMetaData方法获得(数据库URL、用户名、JDBC驱动程序名称等)
ResultSet对象的getMetaData方法于获取(表的列数、列名、列类型等)
事务
Connection conn = openConnection();
try {
// 关闭自动提交:获取到Connection连接后,总是处于“自动提交”模式,也就是默认一条语句一次提交
conn.setAutoCommit(false);
// 执行多条SQL语句:
insert(); update(); delete();
// 提交事务:
conn.commit();
} catch (SQLException e) {
// 回滚事务:
conn.rollback();
} finally {
conn.setAutoCommit(true);//把Connection对象的状态恢复到初始值
conn.close();
}
隔离级别
参见 事务 故障恢复
MySQL的默认隔离级别是REPEATABLE READ
// 设定隔离级别为READ COMMITTED:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
连接池
hikariCP
python pymysql库
连接数据库 执行操作
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
db='my_db',
passwd='123123',
charset='utf8'
)
import pymysql
db = pymysql.connect('localhost', 'root', '123456', 'conv19_datas')
# return pymysql.connections.Connection
cursor = db.cursor()
cursor.execute('show tables;')
cursor.executemany(sql,data1)
data = cursor.fetchone() # 获取一条记录
#'fetchall', 'fetchmany', 'fetchone'
db.close()
事务提交回滚
try:
with conn.cursor() as cursor:
sql="insert into sns_insight_log(LOG_DATE,BIZ_ID,MEDIA_TYPE,ITEM_NAME,DEC_VALUE,UPD_DATE) values(%s,%s,%s,%s,%s,%s)"
cursor.executemany(sql,data)
conn.commit() #不带这个可能出错
except:
conn.rollback()
finally:
conn.close()
简单数据库操作举例
create database `my_db` default character set utf8 collate utf8_general_ci
'''
create table user (
id int(16) AUTO_INCREMENT PRIMARY KEY,
username varchar(32) not null,
password varchar(32) not null
)
ENGINE = MyISAM'''
insert into user (username, password) values ('teble', 'teble')
select id, username, password from user ORDER BY id DESC
update user set username = 'root', password = 'root' WHERE username = 'teble0'
cursor.execute("select Cname from Teacher LEFT JOIN Course on Teacher.Tno=Course.Tno where Tname=%s",tname)
cs.execute("update Course set Cname=%s where Cno=%s ;",[a,b])
delete from user where username = 'teble1'
参数化接口
cur.execute(“SELECT * FROM %s WHERE city = %s”,[s1,s2])
cur.execute(“SELECT * FROM %s WHERE city = %s”,data)
批量data是嵌套的列表或元组 [(),()] [[],[]]都行
cursor.execute('insert into nudt(year,province) values (%s,"%s")' % (year,province))
sql = “INSERT INTO user VALUES(%(username)s, %(password)s, %(email)s)”
value = {“username”:zhangsan,
“password”:123456,
“email”:123456@ouvps.com}
cur.execute(sql, value)