板凳-------Mysql cookbook学习 (七)
2.5 处理语句中的特殊字符和null值 133/951 Saturday, May 24, 2025
有两种方法可用于处理特殊字符如引号和反斜线, 以及特殊值如NULL
1 在语句字符串中使用占位符来指代数据值,执行语句时数据值将绑定到占位符上。
2 使用一个引用函数将数据值转化为适用于语句字符串的一种安全格式。
特殊字条还出现于这里没有涵盖的其他上下文中。
这里所述的占位符和引用技术仅用于数据值,而不适用标识符诸如数据库或表名。
涵盖了将特殊字符插入你的数据库中的主题。将从你的数据库中返回值的特殊字符进行转换以显示在不同的上下文中。
如正在生成HTML页面,其中包含从数据库中取出的值,需要将那引起值中的<和>字符转化为HTML元素<;和> 以确保正确地显示。
使用占位符
占位符可以免于逐字输入数据值到SQL语句中。一个普通的占位符字符是 ?。
Insert into profile (name, birth, color, foods, cats)
Values(?, ?, ?, ?, ?)
# Cell 1: Run the script using %run
%run c:/lib/mcb/Cookbook.py# Cell 2: Use the loaded function
import sys
import MySQLdbtry:conn = connect() # This function should be defined in Cookbook.pyprint("Connected to database")cursor = conn.cursor()cursor.execute("SELECT * FROM profile WHERE cats = %s", (2,))for row in cursor.fetchall(): # Fixed typo: fetcall -> fetchallprint(row)cursor.close()conn.close() # Don't forget to close the connectionexcept MySQLdb.Error as e:print(f"Connection failed: Error {e.args[0]} ({e.args[1]})")sys.exit(1)Connected to database
(8, 'Shepard', datetime.date(1975, 9, 2), 'black', 'curry,pizza', 2) Sunday, May 25, 2025 141/951
Python占位符机制在数据值被绑定到语句字符串时,如有必要会在这些值周围加上引号,所以在字符串中不要在%s格式化符号周围放置引号。
一个可选的方法是使用literal()方法。
# Cell 1: Run the script using %run
%run c:/lib/mcb/Cookbook.py# Cell 2: Use the loaded function
import sys
import MySQLdbtry:conn = connect()print("Connected to database")cursor = conn.cursor()# Use parameterized query (proper way)stmt = """INSERT INTO profile (name, birth, color, foods, cats)VALUES (%s, %s, %s, %s, %s)"""params = ("De'Mont", "1980-12-12", None, "eggroll", 8)cursor.execute(stmt, params)# INSERT doesn't return rows, so fetchall() isn't neededprint(f"Rows affected: {cursor.rowcount}")conn.commit() # Important for INSERT operationscursor.close()conn.close()except MySQLdb.Error as e:print(f"Error: {e.args[1]}")if 'conn' in locals(): # Check if connection was establishedconn.rollback() # Rollback on error
sys.exit(1)Connected to database
Rows affected: 1
mysql> use cookbook
Database changed
mysql> select * from profile;
+----+---------+------------+-------+-----------------------+------+
| id | name | birth | color | foods | cats |
+----+---------+------------+-------+-----------------------+------+
| 1 | Fred | 1970-04-13 | black | lutefisk,fadge,pizza | 1 |
| 2 | Mort | 1969-09-30 | white | burrito,curry,eggroll | 3 |
| 3 | Brit | 1957-12-01 | red | burrito,curry,pizza | 1 |
| 4 | Carl | 1973-11-02 | red | eggroll,pizza | 4 |
| 5 | Sean | 1963-07-04 | blue | burrito,curry | 5 |
| 6 | Alan | 1965-02-14 | red | curry,fadge | 1 |
| 7 | Mara | 1968-09-17 | green | lutefisk,fadge | 1 |
| 8 | Shepard | 1975-09-02 | black | curry,pizza | 2 |
| 9 | Dick | 1952-08-20 | green | lutefisk,fadge | 0 |
| 10 | Tony | 1960-05-01 | white | burrito,pizza | 0 |
| 11 | Alison | 1973-01-12 | blue | eggroll | 4 |
| 12 | De'Mont | 1973-01-12 | NULL | eggroll | 4 |
| 13 | De'Mont | 1973-01-12 | NULL | eggroll | 4 |
| 14 | De'Mont | 1973-01-12 | NULL | eggroll | 4 |
| 15 | De'Mont | 1973-01-12 | NULL | eggroll | 4 |
| 16 | De'Mont | 1973-01-12 | NULL | eggroll | 4 |
| 17 | Amabel | NULL | NULL | NULL | NULL |
| 18 | De'Mont | 1980-12-12 | NULL | eggroll | 8 |
+----+---------+------------+-------+-----------------------+------+
18 rows in set (0.00 sec)
2.6 处理标识符中特殊字符
错误
mysql> create table `some table` (i int);
ERROR 1050 (42S01): Table 'some table' already exists
• 错误代码:1050 (表已存在错误)
• 表名:some table(注意这是用反引号包裹的表名,因为包含空格)
解决方案
方案1:直接删除旧表(如果数据可丢弃)
DROP TABLE IF EXISTS `some table`;
CREATE TABLE `some table` (i int);
方案2:检查表结构(如果你想确认是否要覆盖)
SHOW CREATE TABLE `some table`;
方案3:使用临时表过渡(如果需要保留数据)
-- 先重命名旧表
RENAME TABLE `some table` TO `some table_old`;-- 再创建新表
CREATE TABLE `some table` (i int);-- 最后迁移数据(如果需要)
INSERT INTO `some table` (i)
SELECT i FROM `some table_old` WHERE ...;
特别提醒
1. 表名包含空格时必须使用反引号(`)
2. 生产环境操作前建议备份:
CREATE TABLE `some table_backup` AS SELECT * FROM `some table`;
3. 如果想静默执行(不报错),可以加 IF NOT EXISTS:
CREATE TABLE IF NOT EXISTS `some table` (i int);如果一个引用字符出现在标识符中, 引用这样的标识符时内部的引号使用成对的引用字符。Eg. `abc``def` 来引用abc`def.
2.7 识别结果集中的null值
mysql> insert into profile (name) values('Juan');
Query OK, 1 row affected (0.01 sec)mysql> select * from profile where name = 'Juan';
+----+------+-------+-------+-------+------+
| id | name | birth | color | foods | cats |
+----+------+-------+-------+-------+------+
| 19 | Juan | NULL | NULL | NULL | NULL |
+----+------+-------+-------+-------+------+
1 row in set (0.00 sec)
Cell 1: Run the script using %run
%run c:/lib/mcb/Cookbook.py
Cell 2: Use the loaded function
import sys
import MySQLdbtry:conn = connect()print("Connected to database")cursor = conn.cursor()cursor.execute("select name, birth, foods from profile")for row in cursor.fetchall():row = list(row)for i in range(0, len (row)):if row[i] == None:row[i] = "NULL"print("name: %s, birth: %s, foods: %s" % (row[0], row[1], row[2]))cursor.close()conn.close()except MySQLdb.Error as e:print(f"Error: {e.args[1]}")if 'conn' in locals(): # Check if connection was establishedconn.rollback() # Rollback on errorsys.exit(1)
Connected to database
name: Fred, birth: 1970-04-13, foods: lutefisk,fadge,pizza
name: Mort, birth: 1969-09-30, foods: burrito,curry,eggroll
name: Brit, birth: 1957-12-01, foods: burrito,curry,pizza
name: Carl, birth: 1973-11-02, foods: eggroll,pizza
name: Sean, birth: 1963-07-04, foods: burrito,curry
name: Alan, birth: 1965-02-14, foods: curry,fadge
name: Mara, birth: 1968-09-17, foods: lutefisk,fadge
name: Shepard, birth: 1975-09-02, foods: curry,pizza
name: Dick, birth: 1952-08-20, foods: lutefisk,fadge
name: Tony, birth: 1960-05-01, foods: burrito,pizza
name: Alison, birth: 1973-01-12, foods: eggroll
name: De'Mont, birth: 1973-01-12, foods: eggroll
name: De'Mont, birth: 1973-01-12, foods: eggroll
name: De'Mont, birth: 1973-01-12, foods: eggroll
name: De'Mont, birth: 1973-01-12, foods: eggroll
name: De'Mont, birth: 1973-01-12, foods: eggroll
name: Amabel, birth: NULL, foods: NULL
name: De'Mont, birth: 1980-12-12, foods: eggroll
name: Juan, birth: NULL, foods: NULL
2.8 获取连接参数的技术 http://localhost:8888/notebooks/Mysql4.ipynb
命令行使用方式:
使用配置文件中的默认值:python script.py
覆盖特定参数:python script.py -h 127.0.0.1 -u admin -p newpassword
import sys
import os
import configparser
import getopt
import MySQLdb# 配置文件路径
config_path = 'D:\\sql\\Mysql_learning\\config.ini'# 检查配置文件是否存在
if not os.path.exists(config_path):print(f"Error: Config file not found at {config_path}")sys.exit(1)# 读取配置文件
config = configparser.ConfigParser()
try:config.read(config_path)# 从配置文件获取默认值default_host = config.get('database', 'host')default_user = config.get('database', 'user')default_password = config.get('database', 'password')db_name = config.get('database', 'database', fallback='cookbook')
except Exception as e:print(f"Error reading config file: {e}")sys.exit(1)# 解析命令行参数
try:opts, args = getopt.getopt(sys.argv[1:], "h:p:u:", ["host=", "password=", "user="])
except getopt.GetoptError as e:print(f"{sys.argv[0]}: {e}")sys.exit(1)# 初始化变量为配置文件中的默认值
host_name = default_host
user_name = default_user
password = default_password# 用命令行参数覆盖默认值
for opt, arg in opts:if opt in ("-h", "--host"):host_name = argelif opt in ("-p", "--password"):password = argelif opt in ("-u", "--user"):user_name = arg# 尝试连接数据库
try:conn = MySQLdb.connect(host=host_name,user=user_name,passwd=password,db=db_name)print("Connected to MySQL database")# 这里可以添加你的数据库操作代码except MySQLdb.Error as e:print("Cannot connect to server")print("Error:", e.args[1])print("Code:", e.args[0])sys.exit(1)
finally:if 'conn' in locals() and conn: # 更安全的连接检查方式try:conn.close()print("Disconnected from MySQL database")except:pass # 忽略关闭连接时的错误
Jupyter Notebook 内核启动器 (ipykernel)因为 Jupyter 会自动传递 -f 参数给内核。
import os
import configparser
import MySQLdb# --- 配置部分(适合Notebook环境)---
# 配置文件路径
config_path = 'D:/sql/Mysql_learning/config.ini' # 使用正斜杠更安全# 检查配置文件是否存在
if not os.path.exists(config_path):raise FileNotFoundError(f"Config file not found at {config_path}")# 读取配置
config = configparser.ConfigParser()
try:config.read(config_path)db_config = {'host': config.get('database', 'host', fallback='localhost'),'user': config.get('database', 'user'),'password': config.get('database', 'password'),'db': config.get('database', 'database', fallback='cookbook')}
except Exception as e:raise RuntimeError(f"Error reading config: {e}")# --- 数据库连接部分 ---
conn = None
try:# 建立连接conn = MySQLdb.connect(**db_config)print(f"成功连接到MySQL数据库: {db_config['host']}")# 示例:获取数据库版本cursor = conn.cursor()cursor.execute("SELECT VERSION()")version = cursor.fetchone()[0]print(f"MySQL服务器版本: {version}")# 这里添加你的数据库操作代码# 例如:# cursor.execute("SHOW TABLES")# print("数据库表:", cursor.fetchall())except MySQLdb.Error as e:print("数据库连接失败")print(f"错误代码: {e.args[0]}")print(f"错误信息: {e.args[1]}")finally:if conn and conn.open: # 安全关闭连接conn.close()print("数据库连接已关闭")
成功连接到MySQL数据库: localhost
MySQL服务器版本: 8.0.40
数据库连接已关闭
改进的配置处理:
使用 fallback 提供默认值
更清晰的错误提示
路径使用正斜杠(兼容Windows和Linux)
2.9 结论和建议