PHP项目:统计禁区内射门次数对比
下面给你一个完整的 PHP 实现方案,用于统计和对比两支球队在禁区内射门次数。

数据表设计
-- 比赛表
CREATE TABLE matches (
id INT PRIMARY KEY AUTO_INCREMENT,
home_team VARCHAR(50),
away_team VARCHAR(50),
match_date DATE
);
-- 射门记录表
CREATE TABLE shots (
id INT PRIMARY KEY AUTO_INCREMENT,
match_id INT,
team VARCHAR(50), -- 球队名称
player VARCHAR(50), -- 球员
minute INT, -- 比赛分钟
x_coord DECIMAL(5,2), -- 射门位置 x 坐标(米)
y_coord DECIMAL(5,2), -- 射门位置 y 坐标(米)
is_goal TINYINT DEFAULT 0, -- 是否进球
FOREIGN KEY (match_id) REFERENCES matches(id)
);
判断是否在禁区内
标准足球场:长 105m × 宽 68m,禁区为 32m × 16.5m。
<?php
/**
* 判断射门点是否在禁区内
* 以球场左下角为原点 (0,0),右上角为 (105, 68)
* 假定左方球门在 x=0 处,右方球门在 x=105 处
*/
function isInPenaltyArea($x, $y, $side = 'left') {
$penaltyWidth = 16.5; // 禁区宽度(距球门线)
$penaltyHeight = 40.32; // 禁区长度
$fieldWidth = 68;
// 禁区纵向范围(居中)
$yMin = ($fieldWidth - $penaltyHeight) / 2; // 13.84
$yMax = $yMin + $penaltyHeight; // 54.16
if ($side === 'left') {
// 左侧禁区:x ∈ [0, 16.5]
return $x >= 0 && $x <= $penaltyWidth && $y >= $yMin && $y <= $yMax;
} else {
// 右侧禁区:x ∈ [88.5, 105]
return $x >= (105 - $penaltyWidth) && $x <= 105 && $y >= $yMin && $y <= $yMax;
}
}
统计禁区内射门次数
<?php
require 'db.php'; // PDO 连接
/**
* 统计某场比赛禁区内射门次数(按球队分组)
*/
function getPenaltyAreaShots($pdo, $matchId) {
$sql = "SELECT team, x_coord, y_coord, is_goal
FROM shots
WHERE match_id = :mid";
$stmt = $pdo->prepare($sql);
$stmt->execute([':mid' => $matchId]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stats = [];
foreach ($rows as $r) {
$team = $r['team'];
// 判断该队进攻方向(可根据实际项目改用主客队逻辑)
// 这里简化:主队射左门,客队射右门
$side = ($team === $homeTeam) ? 'left' : 'right';
if (!isset($stats[$team])) {
$stats[$team] = ['total' => 0, 'in_box' => 0, 'goals_in_box' => 0];
}
$stats[$team]['total']++;
if (isInPenaltyArea($r['x_coord'], $r['y_coord'], $side)) {
$stats[$team]['in_box']++;
if ($r['is_goal']) $stats[$team]['goals_in_box']++;
}
}
return $stats;
}
对比展示(带百分比)
<?php
$matchId = 1;
$stats = getPenaltyAreaShots($pdo, $matchId);
// 获取球队名
$teams = array_keys($stats);
$homeStat = $stats[$teams[0]] ?? ['in_box'=>0,'total'=>0,'goals_in_box'=>0];
$awayStat = $stats[$teams[1]] ?? ['in_box'=>0,'total'=>0,'goals_in_box'=>0];
function percent($part, $total) {
return $total > 0 ? round($part / $total * 100, 1) . '%' : '0%';
}
?>
<table border="1" cellpadding="8">
<tr>
<th>统计项</th>
<th><?= htmlspecialchars($teams[0]) ?></th>
<th><?= htmlspecialchars($teams[1]) ?></th>
</tr>
<tr>
<td>禁区内射门</td>
<td><?= $homeStat['in_box'] ?></td>
<td><?= $awayStat['in_box'] ?></td>
</tr>
<tr>
<td>总射门</td>
<td><?= $homeStat['total'] ?></td>
<td><?= $awayStat['total'] ?></td>
</tr>
<tr>
<td>禁区内占比</td>
<td><?= percent($homeStat['in_box'], $homeStat['total']) ?></td>
<td><?= percent($awayStat['in_box'], $awayStat['total']) ?></td>
</tr>
<tr>
<td>禁区内进球</td>
<td><?= $homeStat['goals_in_box'] ?></td>
<td><?= $awayStat['goals_in_box'] ?></td>
</tr>
</table>
单条 SQL 版本(推荐)
如果坐标是标准化百分比(0~100)更方便,直接用 SQL 一次统计:
SELECT
team,
COUNT(*) AS total_shots,
SUM(CASE
WHEN x_coord BETWEEN 0 AND 16.5
AND y_coord BETWEEN 13.84 AND 54.16 THEN 1
ELSE 0 END) AS left_box_shots,
SUM(CASE
WHEN x_coord BETWEEN 88.5 AND 105
AND y_coord BETWEEN 13.84 AND 54.16 THEN 1
ELSE 0 END) AS right_box_shots
FROM shots
WHERE match_id = 1
GROUP BY team;
$stmt = $pdo->prepare($sql);
$stmt->execute([':mid' => $matchId]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $r) {
$r['in_box'] = $r['left_box_shots'] + $r['right_box_shots'];
echo "{$r['team']}:禁区内 {$r['in_box']} / 总射门 {$r['total_shots']}\n";
}
结果示例
统计项 主队 客队
─────────────────────────────
禁区内射门 9 4
总射门 15 11
禁区内占比 60.0% 36.4%
禁区内进球 2 1
| 要点 | 说明 |
|---|---|
| 坐标系 | 需明确定义球场原点、方向(左右/上下) |
| 禁区尺寸 | 32m × 16.5m,注意纵向居中偏移 13.84m |
| 方向判断 | 主客队进攻方向相反,需根据半场切换 |
| 数据来源 | 建议用 Opta / StatsBomb 等标准坐标,或统一归一化到 105×68 |
如果你能告诉我坐标格式(如 StatsBomb 的 120×80,或百分比)以及进攻方向逻辑,我可以帮你把判断函数改得更精确。