多表联合查询、脚本使用pymysql
一、多表联合查询
1、数据准备
部门表和员工表
create table dep(
id int primary key auto_increment,
name varchar(20)
);
create table emp(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);
插入数据
insert into dep values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营'),
(205,'保洁');
insert into emp(name,sex,age,dep_id) values
('jason','male',18,200),
('egon','female',48,201),
('kevin','male',18,201),
('nick','male',28,202),
('owen','male',18,203),
('jerry','female',18,204);
2、多表查询
子查询
一条SQL语句的执行结果当成另外一条SQL语句的执行条件
问题:查看员工jason的部门名称:
select dep_id from emp where name='jason'; select * from dep where id=200;
把上述两条SQL语句合并为一条SQL语句
select * from dep where id= (select dep_id from emp where name='jason');
连表查询
把多张实际存在的表按照表关系连成一张虚拟表(不是实际存在的表,而是临时在内存中存的)
select * from emp,dep where emp.dep_id=dep.id;
联表的专业语法
inner join # 内连接,数据只取两张表中共有的数据
left join # 左连接,数据以左表为准,展示左表所有的数据,右表没有的数据使用NULL填充
right join # 右连接,数据以右表为准,展示右表所有的数据,左表没有的数据使用NULL填充
union # 连接多条SQL语句执行的结果
1. inner join
> select * from emp inner join dep where emp.dep_id = dep.id; +----+-------+--------+-----+--------+-----+----------+ | id | name | sex | age | dep_id | id | name | +----+-------+--------+-----+--------+-----+----------+ | 1 | jason | male | 18 | 200 | 200 | 技术 | | 2 | egon | female | 48 | 201 | 201 | 人力资源 | | 3 | kevin | male | 18 | 201 | 201 | 人力资源 | | 4 | nick | male | 28 | 202 | 202 | 销售 | | 5 | owen | male | 18 | 203 | 203 | 运营 | +----+-------+--------+-----+--------+-----+----------+
2. left join
> select * from emp left join dep on emp.dep_id = dep.id; +----+-------+--------+-----+--------+--------+----------+ | id | name | sex | age | dep_id | id | name | +----+-------+--------+-----+--------+--------+----------+ | 1 | jason | male | 18 | 200 | 200 | 技术 | | 2 | egon | female | 48 | 201 | 201 | 人力资源 | | 3 | kevin | male | 18 | 201 | 201 | 人力资源 | | 4 | nick | male | 28 | 202 | 202 | 销售 | | 5 | owen | male | 18 | 203 | 203 | 运营 | | 6 | jerry | female | 18 | 204 | <null> | <null> | +----+-------+--------+-----+--------+--------+----------+
3. right join
> select * from emp right join dep on emp.dep_id = dep.id; +--------+--------+--------+--------+--------+-----+----------+ | id | name | sex | age | dep_id | id | name | +--------+--------+--------+--------+--------+-----+----------+ | 1 | jason | male | 18 | 200 | 200 | 技术 | | 3 | kevin | male | 18 | 201 | 201 | 人力资源 | | 2 | egon | female | 48 | 201 | 201 | 人力资源 | | 4 | nick | male | 28 | 202 | 202 | 销售 | | 5 | owen | male | 18 | 203 | 203 | 运营 | | <null> | <null> | <null> | <null> | <null> | 205 | 保洁 | +--------+--------+--------+--------+--------+-----+----------+
4. union
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; +--------+--------+--------+--------+--------+--------+----------+ | id | name | sex | age | dep_id | id | name | +--------+--------+--------+--------+--------+--------+----------+ | 1 | jason | male | 18 | 200 | 200 | 技术 | | 2 | egon | female | 48 | 201 | 201 | 人力资源 | | 3 | kevin | male | 18 | 201 | 201 | 人力资源 | | 4 | nick | male | 28 | 202 | 202 | 销售 | | 5 | owen | male | 18 | 203 | 203 | 运营 | | 6 | jerry | female | 18 | 204 | <null> | <null> | | <null> | <null> | <null> | <null> | <null> | 205 | 保洁 | +--------+--------+--------+--------+--------+--------+----------+
还可以起别名
select * from emp as e inner join dep as d on e.dep_id=d.id;
二、脚本中使用 pymysql
1、借助于第三方模块操作MySQL
pymysql
mysqlclient----->非常好用,一般情况下很难安装成功
mysqldb
2、安装模块
pip install pymysql;
3、实操模版
import pymysql
# 1 连接mysql服务端
conn = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='Zjz5740##',
db='data2',
charset='utf8',
autocommit=True
)
# 2 获取游标
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 3 执行sql语句
# sql = "insert into emp(name, sex, age, hire_date) values('kevin','male',18, NOW())"
sql = "select * from emp"
# 4 开始执行
affect_rows = cursor.execute(sql)
# print(affect_rows)
# conn.commit() # 修改、添加数据需要commit
# 5 拿到具体数据
# print(cursor.fetchall())
for i in cursor.fetchall():
print(i)
# 6 关闭游标、连接
cursor.close()
conn.close()
注⚠️:cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)中
pymysql.cursors.DictCursor是PyMySQL库提供的一个游标(cursor)类。它用于创建一个游标对象,该对象以字典形式返回查询结果,而不是默认的元组形式。
而默认是 cursor = conn.cursor() 这种方式创建的游标对象,查询结果以元组的形式返回,其中每个元素对应结果中的一个字段的值。
import pymysql
conn = pymysql.connect(host='localhost', user='user', password='password', database='mydb')
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
row = cursor.fetchone()
print(row[0])
大元组套小元组,一个小元组就是一条记录
4、简易登录注册代码使用mysql作为数据库
import pymysql
# 连接数据库
conn = pymysql.connect(
host='localhost',
user='root',
password='Zjz5740##',
database='atm'
autocommit=True # 针对增 改 删自动二次确认
)
# 注册函数
def register():
# 获取用户输入的用户名和密码
username = input("请输入用户名: ")
password = input("请输入密码: ")
# 查询用户是否已存在
cursor = conn.cursor()
sql = "SELECT * FROM user WHERE name = %s" # 避免了sql注入
cursor.execute(sql, (username,))
result = cursor.fetchone()
if result:
print("该用户名已存在,请重新注册!")
else:
# 执行插入语句,将用户信息写入数据库
insert_sql = "INSERT INTO user (name, password) VALUES (%s, %s)"
cursor.execute(insert_sql, (username, password))
conn.commit()
print("注册成功!")
cursor.close()
# 登录函数
def login():
# 获取用户输入的用户名和密码
username = input("请输入用户名: ")
password = input("请输入密码: ")
# 查询用户是否存在
cursor = conn.cursor()
sql = "SELECT * FROM user WHERE name = %s AND password = %s"
cursor.execute(sql, (username, password))
result = cursor.fetchone()
if result:
print("登录成功!")
else:
print("用户名或密码错误!")
cursor.close()
# 主菜单
def main_menu():
while True:
print("1. 注册")
print("2. 登录")
print("3. 退出")
choice = input("请选择操作: ")
if choice == '1':
register()
elif choice == '2':
login()
elif choice == '3':
break
else:
print("无效的选择,请重新输入!")
# 执行主菜单
main_menu()
# 关闭数据库连接
conn.close()
sql 注入
-
基于布尔逻辑的注入:攻击者利用布尔逻辑判断条件,通过构造恶意输入来绕过验证逻辑。例如,在登录页面的用户名和密码字段中输入
' OR '1'='1,攻击者可以成功绕过验证逻辑,因为这个输入会使SQL查询的条件始终为真。 -
基于注释的注入:攻击者使用注释符号(例如
--)注释掉原始SQL查询的一部分,然后在注释之后添加自己的恶意代码。这样,攻击者可以在注释符号后执行任意的SQL代码。 -
UNION注入:攻击者利用UNION操作符将恶意查询结果合并到原始查询中。通过构造恶意的UNION查询,攻击者可以从其他表中提取数据,甚至是敏感信息
import pymysql
conn = pymysql.connect(
host='localhost',
port=3306,
user='root',
passwd='Z740##',
database='atm',
charset='utf8mb4',
autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
username = input('用户名:').strip()
password = input('密码:').strip()
sql = "select * from user where name='%s' and password='%s' " % (username, password)
print(sql)
affect_rows = cursor.execute(sql)
res = cursor.fetchone()
if res:
print('登录成功')
else:
print('登录失败')
第一种是使用-- 把密码那一部分的sql语句给注释掉了

第二种是‘ or 1=’1,把password设置为空,接上一个or 1=‘1’

5、修改mysql数据库库名、表名大写为小写案例
import pymysql
# MySQL 数据库配置
config = {
'user': 'root',
'password': 'password',
'host': 'ip',
'port': 3306,
'cursorclass': pymysql.cursors.DictCursor,
}
db = None # 初始化 db 为 None
try:
# 连接数据库,创建 cursor 对象
db = pymysql.connect(**config)
cursor = db.cursor()
cursor.execute("SHOW DATABASES")
databases = cursor.fetchall()
for database in databases:
db_name = database['Database']
if db_name in ['information_schema', 'performance_schema', 'mysql', 'sys']:
continue
# 库名为大写的情况
if any(c.isupper() for c in db_name):
new_db_name = db_name.lower() # 转换为小写
# 创建新数据库
cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{new_db_name}`")
print(f'Created database {new_db_name}')
# 切换到当前数据库
db.select_db(db_name)
# 获取当前数据库中的所有表
cursor.execute(f"SHOW TABLES IN {db_name}")
tables = cursor.fetchall()
for table in tables:
table_name = table[f'Tables_in_{db_name}']
new_table_name = table_name.lower()
cursor.execute(f"RENAME TABLE `{db_name}`.`{table_name}` TO `{new_db_name}`.`{new_table_name}`")
print(f'Renamed table {table_name} to {new_table_name} in database {new_db_name}')
# 库名为小写的情况
else:
db.select_db(db_name)
cursor.execute(f"SHOW TABLES IN {db_name}")
tables = cursor.fetchall()
for table in tables:
table_name = table[f'Tables_in_{db_name}']
if any(c.isupper() for c in table_name):
new_table_name = table_name.lower()
cursor.execute(f"RENAME TABLE `{db_name}`.`{table_name}` TO `{db_name}`.`{new_table_name}`")
print(f'Renamed table {table_name} to {new_table_name} in database {db_name}')
except pymysql.Error as e:
print(f"Error connecting to MySQL Platform: {e}")
finally:
if db:
db.close()
6、连接数据库 OR 拼接
import pymysql
BLACKLIST_PATTERNS = [r"知情同意书", r"谈话记录", r"查房记录"]
DB_CONFIG = {
'host': "xxxx",
'port': 3306,
'user': 'xxxx',
'password': 'xxxx',
'database': 'xxxx',
'cursorclass': pymysql.cursors.DictCursor
}
def db_connection():
try:
conn = pymysql.connect(**DB_CONFIG)
with conn.cursor() as cursor:
like_clauses = " OR ".join(["DOC_TITLE LIKE %s" for _ in BLACKLIST_PATTERNS])
sql = f"""
SELECT ID, DOC_PATH, DOC_TITLE
FROM glb_emr_file
WHERE {like_clauses}
"""
like_values = [f"%{pattern}%" for pattern in BLACKLIST_PATTERNS]
cursor.execute(sql, like_values)
results = cursor.fetchall()
print(f"查询结果数量: {len(results)}")
for row in results[:100]:
print(row)
except Exception as e:
print("数据库错误:", str(e))
finally:
if 'conn' in locals() and conn.open:
conn.close()
if __name__ == "__main__":
db_connection()
关键写法分析
1、动态构建SQL查询条件
like_clauses = " OR ".join(["DOC_TITLE LIKE %s" for _ in BLACKLIST_PATTERNS])
sql = f"""
SELECT ID, DOC_PATH, DOC_TITLE
FROM glb_emr_file
WHERE {like_clauses}
"""
2、参数化查询
like_values = [f"%{pattern}%" for pattern in BLACKLIST_PATTERNS]
cursor.execute(sql, like_values)
使用参数化查询(%s占位符)而非字符串拼接,有效防止SQL注入
即使BLACKLIST_PATTERNS来自外部输入也是安全的

浙公网安备 33010602011771号