CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
WordPress 开发入门/课时/数据库
RawBlame
课时目录12 节

WordPress 开发入门

  1. 01WordPress 概览
  2. 02环境搭建
  3. 03主题开发
  4. 04区块编辑器
  5. 05插件生态
  6. 06Hooks 与 CPT
  7. 07PHP 架构
  8. 08数据库
  9. 09AJAX 与 Headless
  10. 10安全加固
  11. 11性能与运维
  12. 12综合练习

数据库

Custom tables 与调优。

30 分钟

数据库与查询实战

这一课你将能做到

  1. 画出 WP 核心表的关系图,说清 posts / postmeta / options / users / terms 五大块各存什么
  2. 用 $wpdb 写出带 prepare 的安全查询,并用 dbDelta 规范建自定义表
  3. 用 WP_Query 的 meta_query / tax_query 组合出复杂课程列表,并知道性能坑在哪

为什么要学

WP 把你的所有内容——文章、页面、课程、设置——全部装进十来张 MySQL 表。做数据相关的开发(自定义字段筛选、批量导入、性能调优),绕不开表结构和 $wpdb。这也是 WP 面试区分「会用」和「会做」的分水岭。

人话:wp_posts 像 Java 里的通用实体表,post_type 字段区分文章/页面/课程;wp_postmeta 是典型的 EAV(键值对)扩展表——灵活但天生难索引,meta_query 写狠了就是慢查询之源。

先建立图像

wp_posts (ID, post_title, post_content, post_type, post_status)
   │ 1:N                      │ N:M
   ▼                          ▼
wp_postmeta                 wp_term_relationships
(post_id, meta_key,          (object_id, term_taxonomy_id)
 meta_value)                      │
                                  ▼
wp_options (option_name,     wp_term_taxonomy → wp_terms
 option_value, autoload)     (分类法:category/tag/自定义)
wp_users (ID, user_login) ─1:N→ wp_usermeta

记忆法:四棵树——内容树(posts+meta)、配置树(options)、用户树(users+meta)、词汇树(terms 家族)。

跟着做

步骤 1:先看看库里有什么

wp db tables                        # 列出全部表
wp db query "SELECT post_type, COUNT(*) AS n FROM wp_posts GROUP BY post_type"
wp db size --tables                 # 哪张表最大

打开 phpMyAdmin 逐行看 wp_posts:一篇「课程」就是一行 post_type='course' 的记录,post_content 里存的可能是带区块注释的文本。

步骤 2:$wpdb 三剑客

global $wpdb;

// get_var: 取单个值
$count = $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s",
        'course', 'publish'
    )
);

// get_row: 取一行(对象)
$course = $wpdb->get_row(
    $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", 42 )
);
echo esc_html( $course->post_title );

// get_results: 取多行
$rows = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT p.ID, p.post_title
         FROM {$wpdb->posts} p
         WHERE p.post_type = %s AND p.post_status = %s
         ORDER BY p.post_date DESC
         LIMIT %d",
        'course', 'publish', 10
    )
);

注意 {$wpdb->posts}:用属性而非硬编码 wp_posts,尊重自定义表前缀。%s/%d 占位符是 prepare 的防注入核心。

步骤 3:建一张自定义表

数据量大、查询复杂的场景(如课程学习记录)建独立表,在插件激活钩子里:

register_activation_hook( __FILE__, function () {
    global $wpdb;
    $table   = $wpdb->prefix . 'qrocmq_enrollments';
    $charset = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table (
        id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
        user_id BIGINT(20) UNSIGNED NOT NULL,
        course_id BIGINT(20) UNSIGNED NOT NULL,
        progress TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
        created_at DATETIME NOT NULL,
        PRIMARY KEY  (id),
        KEY user_course (user_id, course_id),
        KEY course_idx (course_id)
    ) $charset;";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta( $sql );
} );

dbDelta 很挑剔:每字段独立一行、PRIMARY KEY 后两个空格、KEY 而非 INDEX——照抄官方示例格式最稳。

步骤 4:WP_Query 组合查询

需求:发布中的「course」,难度=进阶、时长 10-50 小时、属于 Java 分类:

$query = new WP_Query( array(
    'post_type'      => 'course',
    'post_status'    => 'publish',
    'posts_per_page' => 12,
    'paged'          => max( 1, get_query_var( 'paged' ) ),
    'tax_query'      => array(
        array(
            'taxonomy' => 'course_cat',
            'field'    => 'slug',
            'terms'    => 'java',
        ),
    ),
    'meta_query'     => array(
        'relation' => 'AND',
        array(
            'key'   => 'level',
            'value' => 'advanced',
        ),
        array(
            'key'     => 'duration',
            'value'   => array( 10, 50 ),
            'type'    => 'NUMERIC',
            'compare' => 'BETWEEN',
        ),
    ),
) );

数字比较必须 type => NUMERIC,否则按字符串比较,BETWEEN 结果全错——经典暗坑。

步骤 5:看清 wp_options 与 autoload

wp db query "SELECT option_name, LENGTH(option_value) AS size FROM wp_options
             WHERE autoload='yes' ORDER BY size DESC LIMIT 10"

autoload=yes 的选项每次请求全量载入内存。transients(_transient_*)也住这里——塞大数组进 autoload 选项是隐形性能杀手。

讲清楚要点

  1. meta 表是 EAV 模型:一行一个键值对,查「有某字段的文章」需要 JOIN,meta_value 多为 LONGTEXT,LIKE '%x%' 无法走索引——复杂筛选要么控制规模,要么上自定义表。
  2. $wpdb->prepare 是铁律:占位符 %s/%d,永远不拼接变量;表名不能参数化,用 {$wpdb->posts} 属性。
  3. WP_Query 生成的 SQL 是安全的:内部已转义,优先用 WP_Query/Tax_Query 而不是手写 SQL——可读、可缓存(对象缓存自动覆盖)、可被插件过滤。
  4. 读写分离的关注点:wp_options 管「站点级配置」(少而小),postmeta 管「内容级扩展」(随内容量增长),高频聚合统计走自定义表+索引。
  5. 缓存意识:同一请求重复查询相同数据,先查 wp_cache_get / 存 transient,第 11 课性能课展开。

你可能会懵的地方

  • post_content 里那些 是什么? 区块注释:每段内容的类型与属性序列化。这就是「文章也是数据」的直观体现,REST API 返回的 rendered HTML 是渲染后的结果。

  • wp_posts 里大量 post_type='revision'? 每次保存生成的历史版本。通过 WP_POST_REVISIONS 常量限制(define('WP_POST_REVISIONS', 5)),数据清理用 wp post delete $(wp post list --post_type=revision --field=ID)。

  • meta_query 为什么慢? 每个条件都要 JOIN 一次 postmeta 表,条件越多 JOIN 越多;且 meta_value 是文本列,NUMERIC 比较也要函数转换。十条以内的条件组合没问题,上千行数据 + 多层嵌套就要换方案。

  • $wpdb->prefix 有什么用? 支持多站点/一库多站:每站表前缀不同(wp_、wp2_)。硬编码 wp_ 的代码在改前缀的站点上直接崩。

  • 自定义字段该用 postmeta 还是自定义表? 低频读写、跟内容同生命周期 → postmeta;高频统计、需要多列索引、行数百万级 → 自定义表。判断标准是「查询复杂度」。

错法对照

错法问题正解
"WHERE post_title = '$var'"SQL 注入$wpdb->prepare + 占位符
硬编码表名 wp_posts改前缀站点全线崩溃{$wpdb->posts} 与 $wpdb->prefix
meta_query 数字比较不加 type字符串序比较,结果错乱type => 'NUMERIC'
autoload 选项存大数组每次请求全站内存膨胀拆小或改存 transient/对象缓存
用 post_title 做精确匹配查询无索引、慢查 ID 或给标题加索引的自定义表

卡点急救

  1. get_results 返回空:先在 phpMyAdmin 跑同款 SQL;核对 post_status(默认查不到草稿)、表前缀。
  2. prepare 报错参数数量不符:占位符个数与传入参数必须一一对应。
  3. dbDelta 没建出表:格式不合规(多字段一行、小写 KEY、缺两个空格);对照官方示例逐字符检查。

面试常问

  • 取 10 篇文章及其 3 个自定义字段,WP 层与 SQL 层各发生什么? WP_Query 先查 wp_posts(LIMIT 10),meta 通过 update_meta_cache 批量一次 IN 查询取回(并非每篇一查);等价 SQL 是 posts 一次 + postmeta 一次 JOIN/IN,理解这点才能解释为什么 meta 查询要控制数量。

  • 如何优化一个 meta_query 很重的列表页? 手段:限制 posts_per_page、给 meta 查询加 type 校验、热门条件冗余进 taxonomy 或自定义表建索引、结果用 transient/对象缓存、数据量大后迁自定义表。

  • $wpdb->prepare 的作用与局限? 占位符参数化防 SQL 注入;但只保护值,表名/列名/排序方向不能参数化,需白名单校验。

自测三问

  1. 画出 wp_posts 与 postmeta、terms 两类关系,并说明各自的外键字段。
  2. 为什么 meta_value 上做 LIKE '%java%' 慢?两种工程上的解法?
  3. dbDelta 对 SQL 格式有哪些挑剔要求?举三条。

小结一句话

WP 的数据库是「内容树 + 配置树 + 用户树 + 词汇树」四棵树,日常优先 WP_Query,手写必走 prepare,复杂的筛选与统计在数据量增长后诚实地迁往带索引的自定义表。

延伸

  • $wpdb 类参考
  • WP_Meta_Query 参考
  • 下一课:REST API 与 Headless——把 WP 接进本站同款 Next.js 技术栈。
加载本节练习…
← 上一课PHP 架构下一课 →AJAX 与 Headless