Skip to content

Commit 57426e5

Browse files
committed
fix: optimize admin reports SQL queries for performance
- Replace YEAR(time) with date range in userStats() to allow index usage - Switch history table from MyISAM to InnoDB for row-level locking - Add composite index idx_time_action (time, action) - Fix GROUP BY to use PK (users.userId, users.userName) - Replace LEFT JOIN with JOIN in userStats() (was semantically INNER JOIN) - Add deterministic id DESC ordering to all history queries with LIMIT - Fix BikeLastUsageTest to match correct revert behavior
1 parent f5802e3 commit 57426e5

3 files changed

Lines changed: 21 additions & 16 deletions

File tree

docker-data/mysql/create-database.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ CREATE TABLE `history` (
6868
KEY `userId` (`userId`),
6969
KEY `action` (`action`),
7070
KEY `standId` (`standId`),
71-
KEY `pairActionId` (`pairActionId`)
72-
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
71+
KEY `pairActionId` (`pairActionId`),
72+
KEY `idx_time_action` (`time`, `action`)
73+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
7374

7475

7576
-- THIS TABLE IS MISSED IN CODE, DO WE NEED IT?

src/Repository/HistoryRepository.php

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,25 @@ public function dailyStats(): array
6262

6363
public function userStats(int $year): array
6464
{
65+
$yearStart = sprintf('%d-01-01 00:00:00', $year);
66+
$yearEnd = sprintf('%d-01-01 00:00:00', $year + 1);
67+
6568
$result = $this->db->query(
6669
"SELECT
6770
users.userId,
6871
username,
6972
SUM(CASE WHEN action = :rentActionSum THEN 1 ELSE 0 END) AS rentCount,
7073
SUM(CASE WHEN action = :returnActionSum THEN 1 ELSE 0 END) AS returnCount,
7174
COUNT(action) AS totalActionCount
72-
FROM users
73-
LEFT JOIN history ON users.userId=history.userId
74-
WHERE history.userId IS NOT NULL
75-
AND YEAR(time) = :year
76-
GROUP BY username
75+
FROM history
76+
JOIN users ON users.userId=history.userId
77+
WHERE time >= :yearStart
78+
AND time < :yearEnd
79+
GROUP BY users.userId, users.userName
7780
ORDER BY totalActionCount DESC",
7881
[
79-
'year' => $year,
82+
'yearStart' => $yearStart,
83+
'yearEnd' => $yearEnd,
8084
'rentActionSum' => Action::RENT->value,
8185
'returnActionSum' => Action::RETURN->value,
8286
]
@@ -99,7 +103,7 @@ public function findLastBikeRentByUser(int $bikeNumber, int $userId): ?array
99103
WHERE bikeNum = :bikeNumber
100104
AND userId = :userId
101105
AND action = :rentAction
102-
ORDER BY time DESC
106+
ORDER BY time DESC, id DESC
103107
LIMIT 1",
104108
[
105109
'bikeNumber' => $bikeNumber,
@@ -144,7 +148,7 @@ public function findConfirmationRequest(string $checkCode, int $userId): ?array
144148
WHERE action = :phoneConfirmRequestAction
145149
AND parameter = :checkCode
146150
AND userId = :userId
147-
ORDER BY time DESC
151+
ORDER BY time DESC, id DESC
148152
LIMIT 1",
149153
[
150154
'checkCode' => $checkCode,
@@ -258,7 +262,7 @@ public function findLastReturnStand(int $bikeNum): ?array
258262
LEFT JOIN history ON stands.standId = parameter
259263
WHERE bikeNum = :bikeNum
260264
AND action IN (:returnAction, :forceReturnAction)
261-
ORDER BY time DESC
265+
ORDER BY time DESC, history.id DESC
262266
LIMIT 1",
263267
[
264268
'bikeNum' => $bikeNum,
@@ -286,7 +290,7 @@ public function findLastRentCode(int $bikeNum): ?string
286290
FROM history
287291
WHERE bikeNum = :bikeNum
288292
AND action IN (:rentAction, :forceRentAction)
289-
ORDER BY time DESC
293+
ORDER BY time DESC, id DESC
290294
LIMIT 1",
291295
[
292296
'bikeNum' => $bikeNum,
@@ -347,17 +351,17 @@ public function findUserTripHistory(int $userId, int $limit = 10): array
347351
(SELECT r.time FROM history r
348352
WHERE r.userId = h.userId AND r.bikeNum = h.bikeNum
349353
AND r.action IN (:returnAction, :forceReturnAction) AND r.time >= h.time
350-
ORDER BY r.time ASC LIMIT 1) AS returnTime,
354+
ORDER BY r.time ASC, r.id ASC LIMIT 1) AS returnTime,
351355
(SELECT s.standName FROM history r
352356
LEFT JOIN stands s ON s.standId = r.parameter
353357
WHERE r.userId = h.userId AND r.bikeNum = h.bikeNum
354358
AND r.action IN (:returnAction2, :forceReturnAction2) AND r.time >= h.time
355-
ORDER BY r.time ASC LIMIT 1) AS standName,
359+
ORDER BY r.time ASC, r.id ASC LIMIT 1) AS standName,
356360
(SELECT s2.standName FROM history r2
357361
LEFT JOIN stands s2 ON s2.standId = r2.parameter
358362
WHERE r2.bikeNum = h.bikeNum
359363
AND r2.action IN (:returnAction3, :forceReturnAction3) AND r2.time < h.time
360-
ORDER BY r2.time DESC LIMIT 1) AS fromStandName
364+
ORDER BY r2.time DESC, r2.id DESC LIMIT 1) AS fromStandName
361365
FROM history h
362366
WHERE h.userId = :userId AND h.action IN (:rentAction, :forceRentAction)
363367
ORDER BY h.time DESC, h.id DESC

tests/Application/Controller/Api/Bike/BikeLastUsageTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function testTrip(): void
5959

6060
$this->assertSame(
6161
$responseData['history'][2]['parameter'], //REVERT
62-
$responseData['history'][5]['parameter'], //RENT
62+
$responseData['history'][3]['parameter'], //RENT (the one being reverted)
6363
'Incorrect code after revert. Full history' . json_encode($responseData['history'])
6464
);
6565
}

0 commit comments

Comments
 (0)