PHP项目统计"脚后跟传球"成功次数
这是一个比较具体的足球数据分析需求,下面给你一个完整的实现思路和代码示例。

需求分析
要统计"脚后跟传球成功次数",需要明确几个概念:
- 脚后跟传球:传球时使用脚后跟部位(backheel pass)
- 成功:传球后球权仍在己方队员脚下
- 数据来源:需要有传球事件的基础数据
数据表设计
-- 比赛事件表
CREATE TABLE match_events (
id INT PRIMARY KEY AUTO_INCREMENT,
match_id INT NOT NULL,
player_id INT NOT NULL,
team_id INT NOT NULL,
event_type VARCHAR(50) NOT NULL, -- 'pass', 'shot', 'tackle' 等
pass_technique VARCHAR(50) NULL, -- 'normal', 'backheel', 'lob', 'through'
target_player_id INT NULL, -- 接球球员
is_success TINYINT(1) DEFAULT 0, -- 是否成功
minute INT,
second INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_match (match_id),
INDEX idx_player (player_id),
INDEX idx_technique (pass_technique)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心统计代码
基础统计(按球员)
<?php
class PassStatistics
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
/**
* 统计某球员在某场比赛的脚后跟传球成功次数
*/
public function countBackheelPassesByPlayer(int $playerId, int $matchId): array
{
$sql = "SELECT
COUNT(*) AS total_attempts,
SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) AS success_count,
ROUND(
SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) / COUNT(*) * 100
, 2) AS success_rate
FROM match_events
WHERE player_id = :player_id
AND match_id = :match_id
AND event_type = 'pass'
AND pass_technique = 'backheel'";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
':player_id' => $playerId,
':match_id' => $matchId,
]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: [
'total_attempts' => 0,
'success_count' => 0,
'success_rate' => 0,
];
}
/**
* 统计整场比赛所有球员的脚后跟传球排行
*/
public function topBackheelPassers(int $matchId, int $limit = 10): array
{
$sql = "SELECT
e.player_id,
p.name AS player_name,
t.name AS team_name,
COUNT(*) AS attempts,
SUM(CASE WHEN e.is_success = 1 THEN 1 ELSE 0 END) AS successes
FROM match_events e
JOIN players p ON p.id = e.player_id
JOIN teams t ON t.id = e.team_id
WHERE e.match_id = :match_id
AND e.event_type = 'pass'
AND e.pass_technique = 'backheel'
GROUP BY e.player_id, p.name, t.name
ORDER BY successes DESC, attempts DESC
LIMIT :limit";
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':match_id', $matchId, PDO::PARAM_INT);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
调用示例
$pdo = new PDO('mysql:host=localhost;dbname=football;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$stat = new PassStatistics($pdo);
// 查询梅西在某场比赛的脚后跟传球
$result = $stat->countBackheelPassesByPlayer(playerId: 10, matchId: 1001);
echo "脚后跟传球尝试:{$result['total_attempts']} 次\n";
echo "成功次数:{$result['success_count']} 次\n";
echo "成功率:{$result['success_rate']}%\n";
// 比赛脚后跟传球排行
$ranks = $stat->topBackheelPassers(1001, 5);
foreach ($ranks as $r) {
printf("%-15s (%s): %d/%d\n", $r['player_name'], $r['team_name'], $r['successes'], $r['attempts']);
}
关键设计要点
数据采集阶段
- 人工标注:Scout 或 Opta 类数据提供商手动/半自动记录
- AI 识别:通过视频分析模型识别脚后跟触球动作(较难,需要视觉模型)
- 重点:
pass_technique字段必须准确标注为backheel
"成功"的定义
// 定义清晰的成功标准
$isSuccess = (
$event['target_player_id'] === $event['teammate_id'] && // 队友接到
$event['next_event_team'] === $event['team_id'] // 下一次触球是己方
);
常见扩展统计
/**
* 按位置/赛季/赛事维度统计
*/
public function countByDimension(array $filters)
{
$sql = "SELECT
COUNT(*) AS total,
SUM(is_success) AS success
FROM match_events e
JOIN matches m ON m.id = e.match_id
WHERE e.pass_technique = 'backheel'
AND e.event_type = 'pass'";
$params = [];
if (!empty($filters['season_id'])) {
$sql .= " AND m.season_id = :season_id";
$params[':season_id'] = $filters['season_id'];
}
if (!empty($filters['competition_id'])) {
$sql .= " AND m.competition_id = :competition_id";
$params[':competition_id'] = $filters['competition_id'];
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
实战建议
| 环节 | 建议 |
|---|---|
| 数据源 | 如果没有专业数据,可用 StatsBomb Open Data(含 pass_technique 字段) |
| 字段命名 | 用 technique 而不是 body_part(脚后跟本质是技术动作) |
| 性能优化 | 对 (match_id, event_type, pass_technique) 建复合索引 |
| 缓存 | 赛季级汇总数据用 Redis 缓存 |
| 幂等 | 事件 ID 唯一,避免重复导入导致统计翻倍 |
StatsBomb 数据示例
如果你使用 StatsBomb 开放数据,JSON 结构大致是:
{
"type": {"name": "Pass"},
"pass": {
"technique": {"name": "Backheel"},
"outcome": null,
"recipient": {"id": 5503, "name": "Jordi Alba"}
}
}
解析入库时:
$technique = $event['pass']['technique']['name'] ?? 'Normal'; $isSuccess = !isset($event['pass']['outcome']); // 无 outcome 表示成功 // 入库时 pass_technique 统一转小写存储 $passTechnique = strtolower($technique); // 'backheel'
如果你的具体场景是 StatsBomb 数据导入 + 统计、自建视频标注系统,或者是 已有数据库要加统计接口,可以告诉我,我给你更针对性的代码。