开发WordPress订阅用户删除插件遇报错:子查询返回多行
解决WordPress插件删除过期订阅用户的数据库错误
错误原因
你的SQL查询中,子查询(SELECT user_id FROM $wpdb->users WHERE user_registered < NOW() - INTERVAL $days DAY)会返回多个用户ID,但原代码直接用AND连接这个子查询,数据库无法处理单个字段和多行结果的匹配,因此抛出“Subquery returns more than 1 row”错误。
修正方案:使用IN匹配多行结果
你尝试用IN的思路是对的,但语法有误。正确的写法是将子查询放入IN()中,让user_id匹配子查询返回的所有ID。同时要规范使用$wpdb->prepare绑定所有变量,避免SQL注入风险:
global $wpdb; require_once(ABSPATH . 'wp-admin/includes/user.php'); $role = "subscriber"; $reassign = 1; $days = 30; $this_role = "[[:<:]]" . $role . "[[:>:]]"; // 修正后的查询:用IN匹配子查询返回的多个用户ID $query = $wpdb->prepare(" SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s AND user_id IN ( SELECT user_id FROM $wpdb->users WHERE user_registered < NOW() - INTERVAL %d DAY ) AND meta_value RLIKE %s", $wpdb->prefix . 'capabilities', $days, $this_role ); if ($users_of_this_role = $wpdb->get_results($query, ARRAY_N)) { foreach ($users_of_this_role as $user_id) { wp_delete_user($user_id[0], $reassign); } }
更优方案:使用JOIN提升查询效率
当用户量较大时,子查询的性能不如表关联。推荐使用JOIN直接关联users和usermeta表,逻辑更清晰且效率更高:
global $wpdb; require_once(ABSPATH . 'wp-admin/includes/user.php'); $role = "subscriber"; $reassign = 1; $days = 30; $this_role = "[[:<:]]" . $role . "[[:>:]]"; // 使用JOIN关联用户表与用户元数据表 $query = $wpdb->prepare(" SELECT u.ID AS user_id FROM $wpdb->users u JOIN $wpdb->usermeta um ON u.ID = um.user_id WHERE um.meta_key = %s AND um.meta_value RLIKE %s AND u.user_registered < NOW() - INTERVAL %d DAY", $wpdb->prefix . 'capabilities', $this_role, $days ); if ($users_of_this_role = $wpdb->get_results($query, ARRAY_N)) { foreach ($users_of_this_role as $user_id) { wp_delete_user($user_id[0], $reassign); } }
额外优化点
- 移除了手动定义
ABSPATH的代码:WordPress加载插件时已自动定义该常量,手动定义可能导致路径冲突。 - 所有变量通过
$wpdb->prepare绑定:确保查询安全,防止SQL注入。 - 为循环添加大括号:符合PHP编码规范,避免潜在的逻辑歧义。
内容的提问来源于stack exchange,提问作者Edward
相关产品推荐
相关产品推荐

