php项目统计头球争顶成功率如何?

wen PHP项目 2

本文目录导读:

php项目统计头球争顶成功率如何?

  1. 第一步:数据库设计(MySQL / PostgreSQL)
  2. 第二步:后端 PHP 计算逻辑(核心)
  3. 第三步:前端展示(HTML + 简易 CSS)
  4. 进阶优化建议

在 PHP 项目中统计头球争顶成功率,核心在于数据建模计算逻辑,成功率通常定义为:成功次数 / 总争顶次数

以下是详细的实现方案,涵盖数据库设计、后端计算和前端展示(含示例代码)。


第一步:数据库设计(MySQL / PostgreSQL)

你需要两张核心表:players(球员表)和 events(赛事事件表)。

  1. players 表(球员基本信息)

    CREATE TABLE players (
        id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        position VARCHAR(20)
    );
  2. events 表(记录每一次争顶事件) 关键字段是 event_type(事件类型)和 is_success(是否成功)。

    CREATE TABLE match_events (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        match_id INT UNSIGNED NOT NULL,          -- 比赛ID
        player_id INT UNSIGNED NOT NULL,         -- 球员ID
        event_type ENUM('header', 'shot', 'pass') NOT NULL, -- 事件类型
        is_success TINYINT(1) NOT NULL DEFAULT 0, -- 1=成功, 0=失败
        created_at DATETIME NOT NULL
    );

    注意:如果未来要统计“头球射门成功率”和“头球传球成功率”,建议增加一个 sub_type 字段,区分 clearance(解围)、shot(射门)、assist(助攻)等,也可以用 event_name 字符串代替 ENUM,便于扩展。


第二步:后端 PHP 计算逻辑(核心)

场景 1:统计单个球员的赛季/累计成功率

<?php
function getPlayerHeaderSuccessRate(PDO $pdo, int $playerId, ?int $seasonId = null): array
{
    // 1. 查询基础数据(只统计头球争顶事件)
    $sql = "SELECT 
                SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) AS success_count,
                COUNT(*) AS total_count
            FROM match_events e
            INNER JOIN matches m ON e.match_id = m.id
            WHERE e.player_id = :player_id 
              AND e.event_type = 'header'
              AND (:season_id IS NULL OR m.season_id = :season_id)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':player_id' => $playerId,
        ':season_id' => $seasonId
    ]);
    $result = $stmt->fetch(PDO::FETCH_ASSOC);
    $total = (int)$result['total_count'];
    $success = (int)$result['success_count'];
    // 2. 计算成功率(避免除零错误)
    $rate = 0;
    if ($total > 0) {
        $rate = round(($success / $total) * 100, 1); // 保留1位小数
    }
    return [
        'player_id'     => $playerId,
        'total_attempts'=> $total,
        'success_count' => $success,
        'failure_count' => $total - $success,
        'success_rate'  => $rate, // 65.5
    ];
}
// 使用示例
$pdo = new PDO('mysql:host=localhost;dbname=football_stats', 'user', 'pass');
$stats = getPlayerHeaderSuccessRate($pdo, 101, 2024);
print_r($stats);
/*
输出:
Array
(
    [player_id] => 101
    [total_attempts] => 50
    [success_count] => 33
    [failure_count] => 17
    [success_rate] => 66
)
*/
?>

场景 2:统计全队所有球员的排行榜(展示用)

<?php
function getTeamHeaderStats(PDO $pdo, int $teamId, int $matchRange = 10): array
{
    // 获取最近 N 场比赛(通过 match_id 关联)
    $sql = "SELECT 
                p.id, p.name,
                SUM(CASE WHEN e.is_success = 1 THEN 1 ELSE 0 END) AS success_count,
                COUNT(e.id) AS total_count
            FROM match_events e
            INNER JOIN players p ON e.player_id = p.id
            INNER JOIN matches m ON e.match_id = m.id
            WHERE m.home_team_id = :team_id OR m.away_team_id = :team_id
            AND e.event_type = 'header'
            AND m.match_date >= DATE_SUB(NOW(), INTERVAL :range DAY) -- 或者限定赛季
            GROUP BY p.id, p.name
            ORDER BY total_count DESC, success_count DESC
            LIMIT 20";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([':team_id' => $teamId, ':range' => $matchRange]);
    $players = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // 计算百分比并格式化
    foreach ($players as &$player) {
        $player['success_rate'] = $player['total_count'] > 0 
            ? round(($player['success_count'] / $player['total_count']) * 100, 1)
            : 0;
    }
    return $players;
}
?>

第三步:前端展示(HTML + 简易 CSS)

前端主要是格式化显示,重点突出“成功率”数字。

<!DOCTYPE html>
<html>
<head>
    <style>
        .stat-table { border-collapse: collapse; width: 100%; }
        .stat-table th, .stat-table td { border: 1px solid #ddd; padding: 8px; text-align: center; }
        .stat-table th { background-color: #f2f2f2; }
        .rate-good { color: green; font-weight: bold; }      <!-- 高成功率 -->
        .rate-mid { color: orange; }                         <!-- 中成功率 -->
        .rate-low { color: red; }                            <!-- 低成功率 -->
    </style>
</head>
<body>
<h3>队内头球争顶成功率排行榜</h3>
<table class="stat-table">
    <thead>
        <tr>
            <th>排名</th>
            <th>球员</th>
            <th>总争顶次数</th>
            <th>成功次数</th>
            <th>失败次数</th>
            <th>成功率</th>
        </tr>
    </thead>
    <tbody>
    <?php 
    // 假设通过 API 或直接调用了上面的 getTeamHeaderStats() 函数
    $stats = getTeamHeaderStats($pdo, 1, 30);
    $rank = 1;
    foreach ($stats as $player): 
        // 根据成功率设置CSS类
        $rateClass = 'rate-low';
        if ($player['success_rate'] >= 60) $rateClass = 'rate-good';
        elseif ($player['success_rate'] >= 40) $rateClass = 'rate-mid';
    ?>
        <tr>
            <td><?php echo $rank++; ?></td>
            <td><?php echo htmlspecialchars($player['name']); ?></td>
            <td><?php echo $player['total_count']; ?></td>
            <td><?php echo $player['success_count']; ?></td>
            <td><?php echo $player['total_count'] - $player['success_count']; ?></td>
            <td class="<?php echo $rateClass; ?>">
                <?php echo $player['success_rate']; ?>%
            </td>
        </tr>
    <?php endforeach; ?>
    </tbody>
</table>
</body>
</html>

进阶优化建议

缓存计算(性能优化) 如果数据量大,不要每次请求都跑 COUNTSUM,可以建一张 player_stats_cache 表,定期更新。

CREATE TABLE player_stats_cache (
    player_id INT PRIMARY KEY,
    total_headers INT DEFAULT 0,
    successful_headers INT DEFAULT 0,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

区分“头球争顶”和“头球射门” 如果产品需求要分别统计“防守解围头球”和“进攻射门头球”,需要修改 event_type 或增加 sub_type 字段。

定义“成功”的判定标准 这是统计的核心难点,需要明确:

  • 成功:球员用头将球顶向本方/对方球门方向、或者稳妥解围、或者传给队友形成有效控制。
  • 失败:顶球出界、顶给对方、或者根本没顶到(冒顶)。

建议:在事件录入页面,由数据录入员根据录像回放手动标记 is_success,这是最准确的。

跨赛季统计 在查询时通过 INNER JOIN matches 关联 season_id 字段进行过滤即可,参数化查询可以支持灵活筛选。


这个功能的核心不是 PHP 语法,而是明确统计口径,推荐使用单项事件(每次争顶一条记录)来存储,通过简单的 COUNTSUM 聚合,就能得到准确数据,且方便扩展到助攻、拦截等其他数据项。

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