php项目统计传球成功率哪队更高?

wen PHP项目 2

PHP项目:统计哪支球队传球成功率更高

下面是一个完整的PHP实现方案,包括数据模型、计算逻辑和示例。

php项目统计传球成功率哪队更高?

核心概念

传球成功率 = 成功传球次数 / 总传球次数 × 100%

数据结构设计

数据库表结构(MySQL)

-- 球队表
CREATE TABLE teams (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL
);
-- 比赛表
CREATE TABLE matches (
    id INT PRIMARY KEY AUTO_INCREMENT,
    home_team_id INT,
    away_team_id INT,
    match_date DATE
);
-- 球员表
CREATE TABLE players (
    id INT PRIMARY KEY AUTO_INCREMENT,
    team_id INT,
    name VARCHAR(50)
);
-- 传球事件表(核心)
CREATE TABLE passes (
    id INT PRIMARY KEY AUTO_INCREMENT,
    match_id INT,
    player_id INT,
    team_id INT,
    is_successful TINYINT(1) DEFAULT 1,  -- 1成功 0失败
    pass_time DATETIME
);
CREATE INDEX idx_team ON passes(team_id);
CREATE INDEX idx_match ON passes(match_id);

PHP 核心计算代码

方式1:纯 SQL 聚合(推荐,性能最好)

<?php
class PassStats
{
    private PDO $db;
    public function __construct(PDO $db)
    {
        $this->db = $db;
    }
    /**
     * 统计所有球队的传球成功率,返回排序结果
     */
    public function getTeamPassAccuracy(): array
    {
        $sql = "
            SELECT 
                t.id,
                t.name,
                COUNT(p.id) AS total_passes,
                SUM(p.is_successful) AS successful_passes,
                ROUND(SUM(p.is_successful) / COUNT(p.id) * 100, 2) AS accuracy
            FROM teams t
            LEFT JOIN passes p ON p.team_id = t.id
            GROUP BY t.id, t.name
            HAVING total_passes > 0
            ORDER BY accuracy DESC
        ";
        return $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    }
    /**
     * 统计某场比赛两支球队的传球成功率并比较
     */
    public function compareMatch(int $matchId): array
    {
        $sql = "
            SELECT 
                t.id AS team_id,
                t.name AS team_name,
                COUNT(p.id) AS total,
                SUM(p.is_successful) AS success,
                ROUND(SUM(p.is_successful) / COUNT(p.id) * 100, 2) AS accuracy
            FROM passes p
            JOIN teams t ON t.id = p.team_id
            WHERE p.match_id = :match_id
            GROUP BY t.id, t.name
            ORDER BY accuracy DESC
        ";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':match_id' => $matchId]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
        return $this->buildComparison($rows);
    }
    /**
     * 构建比较结果(谁更高、差多少)
     */
    private function buildComparison(array $rows): array
    {
        if (count($rows) < 2) {
            return ['error' => '数据不足,无法比较'];
        }
        $first  = $rows[0];
        $second = $rows[1];
        $diff   = round($first['accuracy'] - $second['accuracy'], 2);
        return [
            'winner'    => $first['team_name'],
            'loser'     => $second['team_name'],
            'winner_acc'=> $first['accuracy'] . '%',
            'loser_acc' => $second['accuracy'] . '%',
            'diff'      => $diff . '%',
            'detail'    => $rows,
        ];
    }
}

方式2:PHP 内存计算(适合小数据量或数据来自 API)

<?php
/**
 * 从传球事件数组计算各队传球成功率
 * @param array $passes 每条含 ['team_id'=>1, 'is_successful'=>1]
 */
function calcPassAccuracy(array $passes): array
{
    $stats = [];
    foreach ($passes as $p) {
        $tid = $p['team_id'];
        if (!isset($stats[$tid])) {
            $stats[$tid] = ['total' => 0, 'success' => 0];
        }
        $stats[$tid]['total']++;
        $stats[$tid]['success'] += (int)$p['is_successful'];
    }
    foreach ($stats as $tid => &$s) {
        $s['accuracy'] = $s['total'] > 0
            ? round($s['success'] / $s['total'] * 100, 2)
            : 0.0;
    }
    unset($s);
    // 按成功率降序
    uasort($stats, fn($a, $b) => $b['accuracy'] <=> $a['accuracy']);
    return $stats;
}

使用示例

<?php
$pdo = new PDO('mysql:host=localhost;dbname=football;charset=utf8mb4', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stats = new PassStats($pdo);
// 1. 联赛所有球队排名
echo "=== 传球成功率排行 ===\n";
foreach ($stats->getTeamPassAccuracy() as $row) {
    printf("%-12s 传球%d次 成功%d次 成功率: %s%%\n",
        $row['name'], $row['total_passes'],
        $row['successful_passes'], $row['accuracy']);
}
// 2. 某场比赛对比
echo "\n=== 比赛 #1 对比 ===\n";
$result = $stats->compareMatch(1);
if (isset($result['error'])) {
    echo $result['error'];
} else {
    printf("胜者: %s (%s)\n", $result['winner'], $result['winner_acc']);
    printf("对手: %s (%s)\n", $result['loser'], $result['loser_acc']);
    printf("领先: %s\n", $result['diff']);
}

输出示例:

=== 传球成功率排行 ===
Manchester City  传球850次 成功780次 成功率: 91.76%
Barcelona        传球920次 成功820次 成功率: 89.13%
...
=== 比赛 #1 对比 ===
胜者: Manchester City (91.76%)
对手: Liverpool (85.30%)
领先: 6.46%

进阶优化建议

场景 建议
大数据量(百万级) 使用汇总表(预聚合):每天/每场定时批量写入 team_match_stats 表,查询时直接读取
多维度统计 加 MINUTE() 分区,可统计前场/后场传球成功率
区分传球类型 加 pass_type(短传/长传/传中),可分组统计
缓存 Redis 缓存排名结果,TTL 5分钟
平局处理 需要在 buildComparison 中增加 accuracy 相等的判断

汇总表示例(性能优化)

CREATE TABLE team_match_stats (
    match_id INT,
    team_id INT,
    total_passes INT,
    successful_passes INT,
    accuracy DECIMAL(5,2),
    PRIMARY KEY (match_id, team_id)
);

每次比赛结束后批量更新一次,查询时直接 SELECT,避免实时扫描海量 passes 明细表。

平局判断补充

if ($first['accuracy'] == $second['accuracy']) {
    return [
        'result' => 'draw',
        'message' => "两队传球成功率相同:{$first['accuracy']}%",
    ];
}

需要我针对特定数据来源(如 CSV 导入、Opta/StatsBomb API、FIFA 数据)或前端展示(图表) 进一步扩展吗?

抱歉,评论功能暂时关闭!