Skip to content

Commit

Permalink
perf: replace SELECT * with specific columns in SQL queries (#3214)
Browse files Browse the repository at this point in the history
This replaces all occurrences of `SELECT *` in SQL queries with
explicit column names. The use of `SELECT *` often retrieves unnecessary
data, increasing query processing time and bandwidth usage. By
specifying the required columns, this change improves performance and
reduces resource usage.
  • Loading branch information
dudantas authored Jan 5, 2025
1 parent b8ce7fc commit cc3a246
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/account/account_repository_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ bool AccountRepositoryDB::getCharacterByAccountIdAndName(const uint32_t &id, con
}

bool AccountRepositoryDB::getPassword(const uint32_t &id, std::string &password) {
auto result = g_database().storeQuery(fmt::format("SELECT * FROM `accounts` WHERE `id` = {}", id));
auto result = g_database().storeQuery(fmt::format("SELECT `password` FROM `accounts` WHERE `id` = {}", id));
if (!result) {
g_logger().error("Failed to get account:[{}] password!", id);
return false;
Expand Down
4 changes: 3 additions & 1 deletion src/creatures/players/cyclopedia/player_title.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ bool PlayerTitle::checkHighscore(uint8_t skill) const {
default:
std::string skillName = g_game().getSkillNameById(skill);
query = fmt::format(
"SELECT * FROM `players` WHERE `group_id` < {} AND `{}` > 10 ORDER BY `{}` DESC LIMIT 1",
"SELECT `id` FROM `players` "
"WHERE `group_id` < {} AND `{}` > 10 "
"ORDER BY `{}` DESC LIMIT 1",
static_cast<int>(GROUP_TYPE_GAMEMASTER), skillName, skillName
);
break;
Expand Down
62 changes: 44 additions & 18 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,10 @@ void Game::resetNpcs() const {

void Game::loadBoostedCreature() {
auto &db = Database::getInstance();
const auto result = db.storeQuery("SELECT * FROM `boosted_creature`");
const auto result = db.storeQuery(
"SELECT `date`, `boostname`, `raceid`, `looktype`, `lookfeet`, `looklegs`, `lookhead`, `lookbody`, `lookaddons`, `lookmount` "
"FROM `boosted_creature`"
);
if (!result) {
g_logger().warn("[Game::loadBoostedCreature] - "
"Failed to detect boosted creature database. (CODE 01)");
Expand Down Expand Up @@ -8514,38 +8517,61 @@ void Game::playerCyclopediaCharacterInfo(const std::shared_ptr<Player> &player,
}

std::string Game::generateHighscoreQueryForEntries(const std::string &categoryName, uint32_t page, uint8_t entriesPerPage, uint32_t vocation) {
std::ostringstream query;
uint32_t startPage = (static_cast<uint32_t>(page - 1) * static_cast<uint32_t>(entriesPerPage));
uint32_t startPage = (page - 1) * static_cast<uint32_t>(entriesPerPage);
uint32_t endPage = startPage + static_cast<uint32_t>(entriesPerPage);

query << "SELECT *, @row AS `entries`, " << page << " AS `page` FROM (SELECT *, (@row := @row + 1) AS `rn` FROM (SELECT `id`, `name`, `level`, `vocation`, `"
<< categoryName << "` AS `points`, @curRank := IF(@prevRank = `" << categoryName << "`, @curRank, IF(@prevRank := `" << categoryName
<< "`, @curRank + 1, @curRank + 1)) AS `rank` FROM `players` `p`, (SELECT @curRank := 0, @prevRank := NULL, @row := 0) `r` WHERE `group_id` < "
<< static_cast<int>(GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`";
Database &db = Database::getInstance();
std::string escapedCategoryName = db.escapeString(categoryName);

std::string query = fmt::format(

This comment has been minimized.

Copy link
@jpaulooliveira

jpaulooliveira Jan 6, 2025

Getting error "Unknown column ''experience'' in field list " sometimes in the console. On an online server with 5 players testing, i will put the field in the list and see if works fine

This comment has been minimized.

Copy link
@murilo09

murilo09 Jan 6, 2025

Contributor

Could you open an issue?

"SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, `entries`, {} AS `page` FROM ("
"SELECT `id`, `name`, `level`, `vocation`, `{}` AS `points`, "
"@curRank := IF(@prevRank = `{}`, @curRank, IF(@prevRank := `{}`, @curRank + 1, @curRank + 1)) AS `rank`, "
"(@row := @row + 1) AS `entries` FROM ("
"SELECT `id`, `name`, `level`, `vocation`, `{}` FROM `players` `p`, "
"(SELECT @curRank := 0, @prevRank := NULL, @row := 0) `r` "
"WHERE `group_id` < {} ORDER BY `{}` DESC"
") `t`",
page, escapedCategoryName, escapedCategoryName, escapedCategoryName, escapedCategoryName, static_cast<int>(GROUP_TYPE_GAMEMASTER), escapedCategoryName
);

if (vocation != 0xFFFFFFFF) {
query << generateVocationConditionHighscore(vocation);
query += generateVocationConditionHighscore(vocation);
}
query << ") `T` WHERE `rn` > " << startPage << " AND `rn` <= " << endPage;

return query.str();
query += fmt::format(") `T` WHERE `entries` > {} AND `entries` <= {}", startPage, endPage);

return query;
}

std::string Game::generateHighscoreQueryForOurRank(const std::string &categoryName, uint8_t entriesPerPage, uint32_t playerGUID, uint32_t vocation) {
std::ostringstream query;
Database &db = Database::getInstance();
std::string escapedCategoryName = db.escapeString(categoryName);
std::string entriesStr = std::to_string(entriesPerPage);

query << "SELECT *, @row AS `entries`, (@ourRow DIV " << entriesStr << ") + 1 AS `page` FROM (SELECT *, (@row := @row + 1) AS `rn`, @ourRow := IF(`id` = "
<< playerGUID << ", @row - 1, @ourRow) AS `rw` FROM (SELECT `id`, `name`, `level`, `vocation`, `" << categoryName << "` AS `points`, @curRank := IF(@prevRank = `"
<< categoryName << "`, @curRank, IF(@prevRank := `" << categoryName << "`, @curRank + 1, @curRank + 1)) AS `rank` FROM `players` `p`, (SELECT @curRank := 0, @prevRank := NULL, @row := 0, @ourRow := 0) `r` WHERE `group_id` < "
<< static_cast<int>(GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`";
std::string query = fmt::format(
"SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, @row AS `entries`, "
"(@ourRow DIV {0}) + 1 AS `page` FROM ("
"SELECT `id`, `name`, `level`, `vocation`, `{1}` AS `points`, "
"@curRank := IF(@prevRank = `{1}`, @curRank, IF(@prevRank := `{1}`, @curRank + 1, @curRank + 1)) AS `rank`, "
"(@row := @row + 1) AS `rn`, @ourRow := IF(`id` = {2}, @row - 1, @ourRow) AS `rw` FROM ("
"SELECT `id`, `name`, `level`, `vocation`, `{1}` FROM `players` `p`, "
"(SELECT @curRank := 0, @prevRank := NULL, @row := 0, @ourRow := 0) `r` "
"WHERE `group_id` < {3} ORDER BY `{1}` DESC"
") `t`",
entriesStr, escapedCategoryName, playerGUID, static_cast<int>(GROUP_TYPE_GAMEMASTER)
);

if (vocation != 0xFFFFFFFF) {
query << generateVocationConditionHighscore(vocation);
query += generateVocationConditionHighscore(vocation);
}
query << ") `T` WHERE `rn` > ((@ourRow DIV " << entriesStr << ") * " << entriesStr << ") AND `rn` <= (((@ourRow DIV " << entriesStr << ") * " << entriesStr << ") + " << entriesStr << ")";

return query.str();
query += fmt::format(
") `T` WHERE `rn` > ((@ourRow DIV {0}) * {0}) AND `rn` <= (((@ourRow DIV {0}) * {0}) + {0})",
entriesStr
);

return query;
}

std::string Game::generateVocationConditionHighscore(uint32_t vocation) {
Expand Down
14 changes: 9 additions & 5 deletions src/io/functions/iologindata_load_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -869,14 +869,18 @@ void IOLoginDataLoad::loadPlayerTaskHuntingClass(const std::shared_ptr<Player> &
}

void IOLoginDataLoad::loadPlayerForgeHistory(const std::shared_ptr<Player> &player, DBResult_ptr result) {
if (!result || !player) {
g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__);
if (!player) {
g_logger().warn("[{}] - Player nullptr", __FUNCTION__);
return;
}

std::ostringstream query;
query << "SELECT * FROM `forge_history` WHERE `player_id` = " << player->getGUID();
if ((result = Database::getInstance().storeQuery(query.str()))) {
auto playerGUID = player->getGUID();

auto query = fmt::format(
"SELECT id, action_type, description, done_at, is_success FROM forge_history WHERE player_id = {}",
playerGUID
);
if ((result = Database::getInstance().storeQuery(query))) {
do {
auto actionEnum = magic_enum::enum_value<ForgeAction_t>(result->getNumber<uint16_t>("action_type"));
ForgeHistory history;
Expand Down
64 changes: 40 additions & 24 deletions src/io/io_bosstiary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ IOBosstiary &IOBosstiary::getInstance() {
}

void IOBosstiary::loadBoostedBoss() {
std::string query = R"SQL(
SELECT `date`, `boostname`, `raceid`, `looktypeEx`, `looktype`,
`lookfeet`, `looklegs`, `lookhead`, `lookbody`,
`lookaddons`, `lookmount`
FROM `boosted_boss`
)SQL";

DBResult_ptr result = g_database().storeQuery(query);
Database &database = Database::getInstance();
auto query = fmt::format("SELECT `date`, `boostname`, `raceid` FROM `boosted_boss`");
DBResult_ptr result = database.storeQuery(query);
if (!result) {
g_logger().error("[{}] Failed to detect boosted boss database. (CODE 01)", __FUNCTION__);
return;
}

auto date = result->getNumber<uint16_t>("date");

Check warning on line 31 in src/io/io_bosstiary.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-debug

unused variable ‘date’ [-Wunused-variable]

Check warning on line 31 in src/io/io_bosstiary.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-debug

unused variable ‘date’ [-Wunused-variable]
auto timeNow = getTimeNow();
auto time = localtime(&timeNow);
auto today = time->tm_mday;
Expand Down Expand Up @@ -73,34 +74,49 @@ void IOBosstiary::loadBoostedBoss() {
uint16_t bossId = randomBossId;

query = fmt::format(
"UPDATE `boosted_boss` SET `date` = '{}', `boostname` = {}, ",
today, g_database().escapeString(bossName)
"UPDATE `boosted_boss` SET `date` = '{}', `boostname` = {}, `raceid` = '{}', ",
today, database.escapeString(bossName), bossId
);
if (const auto &bossType = getMonsterTypeByBossRaceId(bossId); bossType) {

if (const auto bossType = getMonsterTypeByBossRaceId(bossId); bossType) {
query += fmt::format(
"`looktypeEx` = {}, `looktype` = {}, `lookfeet` = {}, `looklegs` = {}, "
"`lookhead` = {}, `lookbody` = {}, `lookaddons` = {}, `lookmount` = {}, ",
bossType->info.outfit.lookTypeEx, bossType->info.outfit.lookType,
bossType->info.outfit.lookFeet, bossType->info.outfit.lookLegs,
bossType->info.outfit.lookHead, bossType->info.outfit.lookBody,
bossType->info.outfit.lookAddons, bossType->info.outfit.lookMount
static_cast<int>(bossType->info.outfit.lookTypeEx),
static_cast<int>(bossType->info.outfit.lookType),
static_cast<int>(bossType->info.outfit.lookFeet),
static_cast<int>(bossType->info.outfit.lookLegs),
static_cast<int>(bossType->info.outfit.lookHead),
static_cast<int>(bossType->info.outfit.lookBody),
static_cast<int>(bossType->info.outfit.lookAddons),
static_cast<int>(bossType->info.outfit.lookMount)
);
}
query += fmt::format("`raceid` = {}", bossId);

if (!g_database().executeQuery(query)) {
g_logger().error("[{}] Failed to update boosted boss in g_database(). (CODE 03)", __FUNCTION__);
query += fmt::format("`raceid` = '{}'", bossId);

if (!database.executeQuery(query)) {
g_logger().error("[{}] Failed to detect boosted boss database. (CODE 03)", __FUNCTION__);
return;
}

query = fmt::format("UPDATE `player_bosstiary` SET `bossIdSlotOne` = 0 WHERE `bossIdSlotOne` = {}", bossId);
if (!g_database().executeQuery(query)) {
g_logger().error("[{}] Failed to reset players' selected boss slot 1. (CODE 03)", __FUNCTION__);
query = fmt::format(
"UPDATE `player_bosstiary` SET `bossIdSlotOne` = 0 WHERE `bossIdSlotOne` = {}",
bossId
);

if (!database.executeQuery(query)) {
g_logger().error("[{}] Failed to reset players selected boss slot 1. (CODE 03)", __FUNCTION__);
}

query = fmt::format("UPDATE `player_bosstiary` SET `bossIdSlotTwo` = 0 WHERE `bossIdSlotTwo` = {}", bossId);
if (!g_database().executeQuery(query)) {
g_logger().error("[{}] Failed to reset players' selected boss slot 2. (CODE 03)", __FUNCTION__);
query = fmt::format(
"UPDATE `player_bosstiary` SET `bossIdSlotTwo` = 0 WHERE `bossIdSlotTwo` = {}",
bossId
);

if (!database.executeQuery(query)) {
g_logger().error("[{}] Failed to reset players selected boss slot 2. (CODE 03)", __FUNCTION__);
return;
}

setBossBoostedName(bossName);
Expand Down

0 comments on commit cc3a246

Please sign in to comment.