使用 Python 执行 SQLite 数据库脚本文件
目录
- 引言:SQL 脚本执行的重要性
- 1.1 为什么需要执行 SQL 脚本?
- 1.2 Python
sqlite3模块概述
- 核心方法:
cursor.executescript()- 2.1
executescript()的工作原理 - 2.2 优势与特点
- 2.1
- 分步实现:执行 SQL 脚本
- 3.1 准备:创建示例 SQL 脚本文件
- 脚本内容示例 (DDL, DML)
- 3.2 连接到 SQLite 数据库
- 3.3 读取 SQL 脚本内容
- 3.4 使用
executescript()执行脚本 - 3.5 事务管理与提交
- 3.1 准备:创建示例 SQL 脚本文件
- Python 代码示例
- 4.1 创建示例 SQL 脚本
- 4.2 执行脚本并验证
- 最佳实践与注意事项
- 5.1 错误处理 (
try-except) - 5.2 脚本来源的安全性
- 5.3
executescript()的事务行为 - 5.4 处理大型 SQL 脚本
- 5.5 自定义脚本解析 (如果
executescript()不满足需求)
- 5.1 错误处理 (
- 综合代码示例:完整的脚本执行与验证流程
- 总结
1. 引言:SQL 脚本执行的重要性
在数据库管理和应用程序开发中,我们经常需要执行一系列相关的 SQL 语句。这些语句可能包括创建表、插入初始数据、修改表结构或定义存储过程等。将这些语句组织在一个单独的 SQL 脚本文件中,可以带来诸多好处:
1.1 为什么需要执行 SQL 脚本?
- 初始化数据库:在应用程序首次启动或部署时,用于创建所有必要的表、索引和视图,并填充初始数据。
- 数据库升级/迁移:管理数据库 schema 的版本,通过执行脚本来应用增量式的更改。
- 数据填充/导入:批量插入数据。
- 测试与重现:快速设置测试环境,或重现特定的数据库状态。
- 可维护性:将 SQL 逻辑从 Python 代码中分离出来,提高代码的可读性和维护性。
1.2 Python sqlite3 模块概述
Python 的 sqlite3 模块是标准库的一部分,提供了与 SQLite 数据库交互的接口。它遵循 Python DB-API 2.0 规范,提供了一致的数据库操作方法。
2. 核心方法:cursor.executescript()
sqlite3 模块为执行多条 SQL 语句的脚本提供了一个非常便捷的方法:cursor.executescript(sql_script_string)。
2.1 executescript() 的工作原理
此方法接受一个包含一个或多个 SQL 语句的字符串。它会自动将字符串根据语句分隔符(通常是分号 ;)拆分成单独的 SQL 语句,并逐一执行它们。
2.2 优势与特点
- 简化操作:无需手动解析 SQL 脚本,
executescript()自动处理语句的分割和执行。 - 内置事务:默认情况下,
executescript()会将其所有操作包装在一个隐式事务中。如果任何一个语句执行失败,整个事务通常会回滚(在 SQLite 3.6.8+ 中)。这确保了数据库状态的一致性。 - 易于使用:只需要读取文件内容并将其作为字符串传递给方法。
3. 分步实现:执行 SQL 脚本
3.1 准备:创建示例 SQL 脚本文件
我们首先需要一个 .sql 文件来演示。这个脚本可以包含 DDL (数据定义语言) 和 DML (数据操作语言) 语句。
示例 init_database.sql 文件内容:
-- init_database.sql
-- Drop tables if they exist to allow clean re-execution
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS departments;
-- Create departments table
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY AUTOINCREMENT,
dept_name TEXT NOT NULL UNIQUE
);
-- Create employees table
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT UNIQUE,
phone_number TEXT,
hire_date DATE,
job_id TEXT,
salary REAL,
dept_id INTEGER,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- Insert initial data into departments
INSERT INTO departments (dept_name) VALUES ('Sales');
INSERT INTO departments (dept_name) VALUES ('Marketing');
INSERT INTO departments (dept_name) VALUES ('Engineering');
INSERT INTO departments (dept_name) VALUES ('Human Resources');
-- Insert initial data into employees
INSERT INTO employees (first_name, last_name, email, hire_date, job_id, salary, dept_id) VALUES
('Alice', 'Smith', 'alice.s@example.com', '2022-01-15', 'SALES_REP', 60000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Sales')),
('Bob', 'Johnson', 'bob.j@example.com', '2021-11-01', 'ENG_LEAD', 95000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Engineering')),
('Charlie', 'Brown', 'charlie.b@example.com', '2023-03-20', 'MARKETING_COORD', 50000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Marketing'));
-- Update an employee's salary
UPDATE employees SET salary = 62000.00 WHERE first_name = 'Alice';
3.2 连接到 SQLite 数据库
使用 sqlite3.connect() 方法建立数据库连接。通常,推荐使用 with 语句作为上下文管理器,以确保连接正确关闭。
import sqlite3
import os
DB_FILE = "company.db"
# with sqlite3.connect(DB_FILE) as conn:
# # ... operations ...
3.3 读取 SQL 脚本内容
需要打开 .sql 文件并读取其所有内容到一个字符串变量中。
def read_sql_script(script_path):
with open(script_path, 'r', encoding='utf-8') as f:
sql_script = f.read()
return sql_script
# sql_content = read_sql_script("init_database.sql")
3.4 使用 executescript() 执行脚本
一旦连接建立且脚本内容读取完毕,就可以通过游标对象调用 executescript()。
# cursor = conn.cursor()
# cursor.executescript(sql_content)
3.5 事务管理与提交
如前所述,executescript() 通常会将其操作包装在内部事务中。然而,最佳实践是在执行任何修改数据库的 DML/DDL 语句后,显式地调用 conn.commit() 来持久化这些更改。如果发生错误,可以通过 conn.rollback() 撤销未提交的更改。在使用 with sqlite3.connect(...) 时,如果没有未捕获的异常,上下文管理器会在退出时自动提交;如果有异常,则会自动回滚。
4. Python 代码示例
4.1 创建示例 SQL 脚本
import sqlite3
import os
import shutil # For cleanup
# --- Configuration ---
SQL_SCRIPT_FILE = "init_database.sql"
DB_FILE = "company.db"
def create_sample_sql_script(filename=SQL_SCRIPT_FILE):
"""Creates a sample SQL script file for demonstration."""
print(f"Creating sample SQL script: {filename}")
sql_content = """
-- init_database.sql
-- Drop tables if they exist to allow clean re-execution
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS departments;
-- Create departments table
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY AUTOINCREMENT,
dept_name TEXT NOT NULL UNIQUE
);
-- Create employees table
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT UNIQUE,
phone_number TEXT,
hire_date DATE,
job_id TEXT,
salary REAL,
dept_id INTEGER,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- Insert initial data into departments
INSERT INTO departments (dept_name) VALUES ('Sales');
INSERT INTO departments (dept_name) VALUES ('Marketing');
INSERT INTO departments (dept_name) VALUES ('Engineering');
INSERT INTO departments (dept_name) VALUES ('Human Resources');
-- Insert initial data into employees
INSERT INTO employees (first_name, last_name, email, hire_date, job_id, salary, dept_id) VALUES
('Alice', 'Smith', 'alice.s@example.com', '2022-01-15', 'SALES_REP', 60000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Sales')),
('Bob', 'Johnson', 'bob.j@example.com', '2021-11-01', 'ENG_LEAD', 95000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Engineering')),
('Charlie', 'Brown', 'charlie.b@example.com', '2023-03-20', 'MARKETING_COORD', 50000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Marketing'));
-- Update an employee's salary
UPDATE employees SET salary = 62000.00 WHERE first_name = 'Alice';
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(sql_content.strip())
print(f" Script '{filename}' created.")
def cleanup(script_filename=SQL_SCRIPT_FILE, db_filename=DB_FILE):
"""Cleans up generated script and database files."""
print("\n--- Cleaning up ---")
if os.path.exists(script_filename):
os.remove(script_filename)
print(f" Deleted SQL script file: {script_filename}")
if os.path.exists(db_filename):
os.remove(db_filename)
print(f" Deleted database file: {db_filename}")
def read_sql_script(script_path):
"""Reads SQL script content from a file."""
try:
with open(script_path, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
print(f"Error: SQL script file '{script_path}' not found.")
return None
except Exception as e:
print(f"Error reading SQL script file: {e}")
return None
4.2 执行脚本并验证
def execute_sql_script(db_filepath, script_filepath):
"""
Connects to the database, reads and executes the SQL script.
"""
print(f"\n--- Executing SQL script '{script_filepath}' on database '{db_filepath}' ---")
sql_script_content = read_sql_script(script_filepath)
if not sql_script_content:
return False
try:
with sqlite3.connect(db_filepath) as conn:
cursor = conn.cursor()
cursor.executescript(sql_script_content)
conn.commit() # Commit changes made by the script
print(" SQL script executed successfully and changes committed.")
return True
except sqlite3.Error as e:
print(f" Database error during script execution: {e}")
return False
except Exception as e:
print(f" An unexpected error occurred: {e}")
return False
def verify_database_state(db_filepath):
"""
Verifies that tables and data were correctly created/inserted by the script.
"""
print(f"\n--- Verifying database state for '{db_filepath}' ---")
try:
with sqlite3.connect(db_filepath) as conn:
cursor = conn.cursor()
# Verify departments table
cursor.execute("SELECT COUNT(*) FROM departments;")
dept_count = cursor.fetchone()[0]
print(f" Number of departments: {dept_count} (Expected: 4)")
# Verify employees table
cursor.execute("SELECT COUNT(*) FROM employees;")
emp_count = cursor.fetchone()[0]
print(f" Number of employees: {emp_count} (Expected: 3)")
# Verify specific data (e.g., Alice's updated salary)
cursor.execute("SELECT salary FROM employees WHERE first_name = 'Alice';")
alice_salary = cursor.fetchone()[0]
print(f" Alice's salary: {alice_salary} (Expected: 62000.0)")
# Fetch all employees to display
cursor.execute("SELECT emp_id, first_name, last_name, salary, dept_name FROM employees JOIN departments ON employees.dept_id = departments.dept_id ORDER BY emp_id;")
employees_data = cursor.fetchall()
print("\n Current Employees:")
print(f"{'ID':<5} {'First Name':<15} {'Last Name':<15} {'Salary':<10} {'Department':<15}")
print(f"{'-'*5:<5} {'-'*15:<15} {'-'*15:<15} {'-'*10:<10} {'-'*15:<15}")
for emp in employees_data:
print(f"{emp[0]:<5} {emp[1]:<15} {emp[2]:<15} {emp[3]:<10.2f} {emp[4]:<15}")
except sqlite3.Error as e:
print(f" Database error during verification: {e}")
except Exception as e:
print(f" An unexpected error occurred during verification: {e}")
# --- Main execution flow ---
# cleanup() # Ensure clean start
# create_sample_sql_script()
# if execute_sql_script(DB_FILE, SQL_SCRIPT_FILE):
# verify_database_state(DB_FILE)
# cleanup() # Clean up after execution
5. 最佳实践与注意事项
5.1 错误处理 (try-except)
在文件读取和数据库操作时,始终使用 try...except 块来捕获潜在的 FileNotFoundError (读取 SQL 脚本时) 和 sqlite3.Error (数据库操作失败时)。这使得程序更健壮。
5.2 脚本来源的安全性
如果 SQL 脚本的路径或内容来自不可信的外部输入(如用户上传),那么执行该脚本可能存在严重的安全风险(SQL 注入、数据泄露、系统破坏等)。务必对脚本内容进行严格的验证和沙箱隔离,或者只执行受信任的、预定义的脚本。在本示例中,我们假设脚本是安全的。
5.3 executescript() 的事务行为
如前所述,executescript() 默认会将整个脚本作为一个事务来执行。这意味着如果脚本中的任何一条语句失败,所有先前的更改都可能被回滚。这对于保持数据库一致性非常有用。然而,如果你的脚本非常庞大且某些部分是独立的,你可能需要手动分割脚本并分批执行和提交,以避免单个大型事务。
5.4 处理大型 SQL 脚本
对于包含数千甚至数百万条语句的巨型 SQL 脚本:
- 内存消耗:一次性将整个脚本读入内存可能会消耗大量内存。
- 性能:单个巨型事务可能会导致长时间的锁定。
- 替代方案:可以考虑自己解析脚本,逐条或分批执行。或者,如果是在 Unix-like 系统上,可以直接通过
subprocess调用sqlite3命令行工具来执行脚本,这通常更适合超大文件。
5.5 自定义脚本解析 (如果 executescript() 不满足需求)
在极少数情况下,如果 executescript() 的默认行为(如语句分隔符处理或事务管理)不符合你的特定需求,你可以自己实现一个脚本解析器。这通常涉及:
- 读取脚本内容。
- 根据分号 (
;) 或其他自定义分隔符将内容分割成单个 SQL 语句。 - 遍历语句列表,使用
cursor.execute()逐条执行。 - 手动管理事务 (
conn.commit()/conn.rollback())。
但对于大多数情况,executescript()已经足够。
6. 综合代码示例:完整的脚本执行与验证流程
import sqlite3
import os
import shutil # For cleanup
# --- Configuration ---
SQL_SCRIPT_FILE = "init_database_full.sql"
DB_FILE = "company_full.db"
# --- 1. Helper Functions ---
def create_sample_sql_script(filename=SQL_SCRIPT_FILE):
"""Creates a sample SQL script file for demonstration."""
print(f"\n--- Creating sample SQL script: {filename} ---")
sql_content = """
-- init_database_full.sql
-- Drop tables if they exist to allow clean re-execution
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS departments;
-- Create departments table
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY AUTOINCREMENT,
dept_name TEXT NOT NULL UNIQUE
);
-- Create employees table
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT UNIQUE,
phone_number TEXT,
hire_date DATE,
job_id TEXT,
salary REAL,
dept_id INTEGER,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- Insert initial data into departments
INSERT INTO departments (dept_name) VALUES ('Sales');
INSERT INTO departments (dept_name) VALUES ('Marketing');
INSERT INTO departments (dept_name) VALUES ('Engineering');
INSERT INTO departments (dept_name) VALUES ('Human Resources');
-- Insert initial data into employees
INSERT INTO employees (first_name, last_name, email, hire_date, job_id, salary, dept_id) VALUES
('Alice', 'Smith', 'alice.s@example.com', '2022-01-15', 'SALES_REP', 60000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Sales')),
('Bob', 'Johnson', 'bob.j@example.com', '2021-11-01', 'ENG_LEAD', 95000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Engineering')),
('Charlie', 'Brown', 'charlie.b@example.com', '2023-03-20', 'MARKETING_COORD', 50000.00, (SELECT dept_id FROM departments WHERE dept_name = 'Marketing'));
-- Update an employee's salary
UPDATE employees SET salary = 62000.00 WHERE first_name = 'Alice';
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(sql_content.strip())
print(f" SQL script '{filename}' created.")
def cleanup_files(script_filename=SQL_SCRIPT_FILE, db_filename=DB_FILE):
"""Deletes generated script and database files."""
print("\n--- Cleaning up files ---")
if os.path.exists(script_filename):
os.remove(script_filename)
print(f" Deleted SQL script file: {script_filename}")
if os.path.exists(db_filename):
os.remove(db_filename)
print(f" Deleted database file: {db_filename}")
def read_sql_script_content(script_path):
"""Reads SQL script content from a file."""
try:
with open(script_path, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
print(f"Error: SQL script file '{script_path}' not found. Cannot proceed.")
return None
except Exception as e:
print(f"Error reading SQL script file '{script_path}': {e}. Cannot proceed.")
return None
def execute_sql_script(db_filepath, script_filepath):
"""
Connects to the database, reads and executes the SQL script using executescript().
Includes comprehensive error handling.
"""
print(f"\n--- Executing SQL script '{script_filepath}' on database '{db_filepath}' ---")
sql_script_content = read_sql_script_content(script_filepath)
if not sql_script_content:
return False # Script content could not be read
conn = None # Initialize conn to None for error handling
try:
conn = sqlite3.connect(db_filepath)
cursor = conn.cursor()
# Executescript handles splitting multiple SQL statements and implicit transactions.
# If an error occurs during script execution, the implicit transaction will typically rollback.
cursor.executescript(sql_script_content)
conn.commit() # Explicitly commit all changes made by the script
print(" SQL script executed successfully and changes committed.")
return True
except sqlite3.Error as e:
print(f" Database error during script execution: {e}")
if conn:
conn.rollback() # Rollback in case of database error
print(" Transaction rolled back due to error.")
return False
except Exception as e:
print(f" An unexpected error occurred: {e}")
if conn:
conn.rollback()
print(" Transaction rolled back due to unexpected error.")
return False
finally:
if conn:
conn.close() # Ensure connection is closed even if an error occurs
def verify_database_state(db_filepath):
"""
Verifies that tables and data were correctly created/inserted by the script.
"""
print(f"\n--- Verifying database state for '{db_filepath}' ---")
conn = None
try:
conn = sqlite3.connect(db_filepath)
conn.row_factory = sqlite3.Row # Allows accessing columns by name
cursor = conn.cursor()
# Verify departments table
cursor.execute("SELECT COUNT(*) FROM departments;")
dept_count = cursor.fetchone()[0]
print(f" Number of departments: {dept_count} (Expected: 4)")
# Verify employees table
cursor.execute("SELECT COUNT(*) FROM employees;")
emp_count = cursor.fetchone()[0]
print(f" Number of employees: {emp_count} (Expected: 3)")
# Verify specific data (e.g., Alice's updated salary)
cursor.execute("SELECT first_name, last_name, salary FROM employees WHERE first_name = 'Alice';")
alice_data = cursor.fetchone()
if alice_data:
print(f" Alice's salary: {alice_data['salary']:.2f} (Expected: 62000.0)")
else:
print(" Alice not found in employees table.")
# Fetch all employees with department name to display
cursor.execute("""
SELECT emp_id, first_name, last_name, salary, dept_name
FROM employees
JOIN departments ON employees.dept_id = departments.dept_id
ORDER BY emp_id;
""")
employees_data = cursor.fetchall()
print("\n Current Employees in Database:")
if employees_data:
print(f"{'ID':<5} {'First Name':<15} {'Last Name':<15} {'Salary':<10} {'Department':<15}")
print(f"{'-'*5:<5} {'-'*15:<15} {'-'*15:<15} {'-'*10:<10} {'-'*15:<15}")
for emp in employees_data:
print(f"{emp['emp_id']:<5} {emp['first_name']:<15} {emp['last_name']:<15} {emp['salary']:<10.2f} {emp['dept_name']:<15}")
else:
print(" No employees found in the database.")
except sqlite3.Error as e:
print(f" Database error during verification: {e}")
except Exception as e:
print(f" An unexpected error occurred during verification: {e}")
finally:
if conn:
conn.close()
# --- Main Program Execution ---
def main():
cleanup_files() # Ensure a clean slate before starting
create_sample_sql_script() # Create the SQL script file
if execute_sql_script(DB_FILE, SQL_SCRIPT_FILE):
verify_database_state(DB_FILE)
print("\n--- Program finished ---")
# cleanup_files() # Uncomment this line to delete the generated files after execution
if __name__ == "__main__":
main()
7. 总结
为您详尽解析了在 Python 中如何使用 sqlite3 模块来执行包含多条 SQL 语句的脚本文件。
核心要点回顾:
cursor.executescript(sql_script_string)是执行 SQL 脚本的核心方法,它会自动解析并执行脚本中的所有语句。- 脚本准备:将一系列 SQL 语句编写到
.sql文件中,并使用 Python 读取其内容。 - 事务管理:
executescript()默认将脚本包装在一个事务中,并通过conn.commit()(或with语句的自动提交) 进行持久化。 - 错误处理:务必使用
try-except sqlite3.Error来捕获数据库错误,并在必要时进行conn.rollback()。 - 安全性:对于来自外部的 SQL 脚本,务必验证其内容以防止安全风险。
通过掌握 cursor.executescript() 方法,您将能够高效、安全地在 Python 应用程序中初始化、升级或批量操作 SQLite 数据库,极大地提高了数据库管理的便捷性。
更多推荐

所有评论(0)