纯SQL无法生成PDF,必须经应用层处理;Python+pandas+pdfkit是简捷方案,需手动安装wkhtmltopdf并注意中文、分页、字体等渲染细节。
SQL 查询结果直接生成 PDF 的现实限制
纯 sql 无法导出 pdf——select 只返回数据,不生成文件格式。mysqldump、psql -c 或 ssms 导出功能默认只支持 csv/excel/txt;pdf 是带样式、分页、字体的渲染产物,必须经过应用层介入。
常见错误现象:ERROR: unrecognized command \"pdf\"(在 psql 里敲 \pdf)、SSMS 点“导出数据”没 PDF 选项、Navicat 导出列表里找不到 PDF 格式。
- 真正能走通的路径只有:SQL 查出数据 → 应用代码(Python/Node.js/Java)组装内容 → 调用 PDF 渲染库生成文件
- 数据库直连工具(如 DBeaver)虽有“导出为 PDF”按钮,但本质是先查数据再本地调用渲染引擎,不是 SQL 本身能力
- 别在存储过程或触发器里试图写 PDF 文件——权限、路径、二进制处理全都不支持
Python + pandas + pdfkit 最简可行路径
适合快速交付内部报表:用 pandas.read_sql 拿数据,转成 HTML 表格,再用 pdfkit 渲染为 PDF。不用学 LaTeX,也不用搭 Web 服务。
关键依赖:pip install pandas pdfkit,并额外安装系统级工具 wkhtmltopdf(pdfkit 的底层引擎)
-
wkhtmltopdf必须手动安装,仅装pdfkit会报错OSError: No wkhtmltopdf executable found - Windows 下下载 exe 后需把路径加进
PATH,macOS 推荐brew install wkhtmltopdf,Linux 用apt install wkhtmltopdf - HTML 模板越简单越稳:避免
flex布局、CSS Grid、外部字体链接——wkhtmltopdf对现代 CSS 支持有限
import pandas as pd
import pdfkit
<p>df = pd.read_sql("SELECT * FROM orders WHERE status = 'shipped' AND created_at > '2024-01-01'", conn)
html = df.to_html(index=False, table_id="report", classes="dataframe")
pdfkit.from_string(html, "shipped_orders.pdf")</p>
WHERE 条件必须下推到 SQL,别在 Python 里过滤
导出前加条件筛选,务必写在 WHERE 子句里,而不是用 df[df['status'] == 'shipped']。否则可能拉回百万行再筛,内存爆掉、网络卡死、数据库连接超时。
- 错误示范:
pd.read_sql("SELECT * FROM logs", conn); df = df[df['level'] == 'ERROR']—— 日志表千万级,光传输就几分钟 - 正确写法:
pd.read_sql("SELECT id, msg, created_at FROM logs WHERE level = 'ERROR' AND created_at > NOW() - INTERVAL '7 days'", conn) - 注意时区:PostgreSQL 用
NOW(),MySQL 用NOW()或CURRENT_TIMESTAMP,SQLite 用datetime('now', '-7 days') - 字符串参数务必用参数化查询,防止 SQL 注入:
pd.read_sql("SELECT * FROM users WHERE dept = %s", conn, params=[dept_name])
中文乱码、表格截断、页眉页脚缺失的典型修复点
pdfkit 默认用英文字体、无分页控制、忽略 HTML 中的 设置,导致中文变方块、长表格被砍、没有标题页。
- 中文字体:在 HTML 字符串开头加
,并确保系统已安装对应字体 - 防截断:给表格加
style="page-break-inside: avoid;",或用pdfkit.from_string(..., options={"page-size": "A3"})换大纸型 - 页眉页脚:用
options={"header-right": "Report-{page}", "footer-center": "Generated on {date}"},日期格式固定为{date}和{page},不支持自定义函数 - 调试技巧:先保存中间 HTML 文件(
open("debug.html", "w").write(html)),浏览器打开确认样式正常,再转 PDF
最常被忽略的是字体和分页——很多人调通了基础流程,一导正式报表就发现中文全成□,或者第一页只有半张表,后面全空白。这两处必须单独验证,不能靠“看起来差不多”。











