PHP项目统计"假动作晃过防守"次数
你这个问题看起来像是 篮球/足球类游戏或体育数据统计系统 的需求,我按"PHP 后端实现一个'假动作晃过防守'次数统计功能"来给你一套可落地的方案。

需求拆解
要统计"假动作晃过防守几次",需要明确:
| 要素 | 说明 |
|---|---|
| 统计对象 | 球员(player_id) |
| 事件类型 | 假动作(feint / fake_move) |
| 判定条件 | 假动作后防守者被晃过(beat_defender) |
| 统计维度 | 单场 / 赛季 / 生涯 |
| 数据来源 | 比赛事件流(event log) |
数据库设计
-- 比赛事件表(核心)
CREATE TABLE match_events (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
match_id INT NOT NULL,
player_id INT NOT NULL,
event_type VARCHAR(32) NOT NULL, -- 'feint', 'pass', 'shot'...
event_result VARCHAR(32) NULL, -- 'beat_defender', 'failed'...
occurred_at DATETIME NOT NULL,
INDEX idx_match_player (match_id, player_id),
INDEX idx_type_result (event_type, event_result)
);
-- 统计结果表(预聚合,提升查询性能)
CREATE TABLE player_feint_stats (
player_id INT NOT NULL,
match_id INT NOT NULL,
feint_total INT DEFAULT 0, -- 假动作总次数
feint_success INT DEFAULT 0, -- 晃过防守次数
updated_at DATETIME,
PRIMARY KEY (player_id, match_id)
);
核心统计 SQL
单场统计某个球员:
SELECT
player_id,
COUNT(*) AS feint_total,
SUM(CASE WHEN event_result = 'beat_defender' THEN 1 ELSE 0 END) AS feint_success,
ROUND(
SUM(CASE WHEN event_result = 'beat_defender' THEN 1 ELSE 0 END)
/ COUNT(*) * 100, 2
) AS success_rate
FROM match_events
WHERE event_type = 'feint'
AND match_id = ?
AND player_id = ?
GROUP BY player_id;
赛季排行榜:
SELECT
player_id,
COUNT(*) AS total_feints,
SUM(event_result = 'beat_defender') AS beat_count
FROM match_events
WHERE event_type = 'feint'
AND occurred_at BETWEEN ? AND ?
GROUP BY player_id
ORDER BY beat_count DESC
LIMIT 20;
PHP 实现(PDO 示例)
class FeintStatsService
{
public function __construct(private PDO $pdo) {}
/** 记录一次假动作事件 */
public function recordFeint(int $matchId, int $playerId, bool $beatDefender): void
{
$stmt = $this->pdo->prepare(
"INSERT INTO match_events (match_id, player_id, event_type, event_result, occurred_at)
VALUES (?, ?, 'feint', ?, NOW())"
);
$stmt->execute([
$matchId,
$playerId,
$beatDefender ? 'beat_defender' : 'failed'
]);
}
/** 查询单场统计 */
public function getMatchStats(int $matchId, int $playerId): array
{
$sql = "SELECT
COUNT(*) AS total,
SUM(event_result = 'beat_defender') AS success,
ROUND(SUM(event_result = 'beat_defender') / COUNT(*) * 100, 2) AS rate
FROM match_events
WHERE event_type = 'feint' AND match_id = ? AND player_id = ?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$matchId, $playerId]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: ['total' => 0, 'success' => 0, 'rate' => 0];
}
/** 赛季排行 */
public function getSeasonRanking(string $start, string $end, int $limit = 20): array
{
$sql = "SELECT player_id,
COUNT(*) AS total,
SUM(event_result = 'beat_defender') AS success
FROM match_events
WHERE event_type = 'feint' AND occurred_at BETWEEN ? AND ?
GROUP BY player_id
ORDER BY success DESC
LIMIT ?";
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(1, $start);
$stmt->bindValue(2, $end);
$stmt->bindValue(3, $limit, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
关键设计要点
-
"晃过"的判定要定义清楚
- 是人工标注事件(推荐),还是通过坐标/时间窗口自动判定(如防守者重心偏移 + 距离拉开)?
- 建议在
event_result里用枚举值:beat_defender/failed/neutral。
-
预聚合 vs 实时计算
- 数据量小(< 百万行):实时
GROUP BY即可。 - 数据量大:用
player_feint_stats预聚合表,事件写入时用事务更新,或定时任务(cron)批量重算。
- 数据量小(< 百万行):实时
-
防止重复计数
- 一次连续动作可能产生多个事件,需要
action_id/possession_id去重。 - 建议加
UNIQUE(action_id)或按"回合"聚合。
- 一次连续动作可能产生多个事件,需要
-
索引优化
- 复合索引
(event_type, match_id, player_id)覆盖绝大多数查询。
- 复合索引
-
缓存
排行榜类查询用 Redis 缓存(TTL 5~10 分钟),避免高频扫表。
如果你问的是别的场景
- 如果是游戏后台:把"假动作"作为技能事件,
event_result由战斗逻辑判定后上报。 - 如果是视频分析系统:PHP 只做数据存储/查询,识别部分用 Python/OpenCV 或第三方 API,通过队列(Redis/RabbitMQ)写入事件表。
需要我针对你的具体场景(比如是体育数据平台、游戏后端、还是视频识别系统)给出更精确的表结构和接口设计吗?