3 Нормированная форма и вопрос по ней.

Rostu

Добрая душа
Сообщения
986
Реакции
622
Здравствуйте, не давно узнал про приведение таблицы в 3 НФ и захотелось попробовать на практике ее спроектировать [Удобнее для данного примера, почему-то мне показался Access] и использовать [Через плагин на SourcePawn]. Получилась такая схемка (Это же точно 3 нф 🤔 )
2020-02-21_22-09-21.png

Я заполнил таблички некоторыми записями для большего понимания проблемы:

2020-02-21_22-20-34.png

2020-02-21_22-17-47.png

2020-02-21_22-19-17.png

С какой проблемой я столкнулся и почему я пишу сюда? - Мой плагин выполняет роль ядра, к которому можно написать модуль, но проблема в способе идентификация значения. Допустим у нас имеются два модуля - один из них - при заходе игрока обновляет его текущий ник, а другой - дату последнего захода.
Для сохранения значений в БД используется
C-подобный:
native void AuthSystem_SetFeatureValue(int iClient, const char[] sFeature, const char[] sValue);

Но для того, чтобы обновить данную запись - необходимо знать `feature_id` и тут встает у меня вопрос, ведь я могу пойти двумя путями:

1) При старте плагина и коннекта его к БД - загружать все значения из`authsystem_features` в ArrayList g_hFeatures, который хранит enum struct FeatureInfo =>
C-подобный:
enum struct FeatureInfo
[
    int id;
    char feature[32];
]
Тогда при использовании
C-подобный:
native void AuthSystem_SetFeatureValue(int iClient, const char[] sFeature, const char[] sValue);
Мне необходимо будет делать что-то типа такого, каждый раз, когда я хочу обновить значение:
C-подобный:
int FindIndexFromName(const char[] sFeature)
{
    FeatureInfo info;

    for(int x = 0; x < g_hFeatures.Length; x++)
    {
        g_hFeatures.GetArray(x, info, sizeof(FeatureInfo));

        if(!strcmp(sFeature, info.feature))
        {
            return x;
        }
    }

    return -1;
}
void SaveValue(int iClient, const char[] sFeature, const char[] sValue)
{
    int index = FindIndexFromName(sFeature);

    if(index == -1)
    {
        return;
    }

    char sQuery[256];
    FormatEx(sQuery, sizeof sQuery, "UPDATE `authsystem_players_features` SET `value` = '%s' WHERE `player_id` = %d AND `feature_id` = %d", g_Player[iClient].id, index);
    DB_TQueryEx(sQuery);
}

Но мне не нравится хранить по факту всю таблицу `authsystem_features` в плагине, по этому, можно пойти по второму способу:

2) Обновлять значение простым объединением двух таблиц - Данный способ мне действительно симпатизирует , но смущает - что такой запрос, по моему мнению, не является эффективным и правильным и нужно делать как-то по другому, по этому и была создана эта тема, чтобы подтвердить мои опасения, или же их развеять

C-подобный:
void SaveValue(int iClient, const char[] sFeature, const char[] sValue)
{
    char sQuery[256];
    FormatEx(sQuery, sizeof sQuery, "UPDATE `authsystem_players_features` p INNER JOIN `authsystem_features` f ON f.`feature` = '%s'  SET p.`value` = '%s' WHERE p.`player_id` =  %d AND p.`feature_id` = f.`id`", sFeature, sValue, g_Player[iClient].id);
    DB_TQueryEx(sQuery);
}
 

Reiko1231

AlexTheRegent
Сообщения
508
Реакции
1,335
Не понятно, что там будет хранится, поэтому нельзя сказать, какой из вариантов лучше.
Оба варианта нормальные, только для первого варианта лучше использовать StringMap вместо ArrayList. С его помощью легко получить индекс по имени (однако он не так эффективен, когда надо найти имя по индексу).
По сути эти варианты являются двумя сторонами медали - первый вариант даёт высокую скорость выполнения (в случае StringMap) ценой оперативной памяти. Второй вариант требует меньше оперативной памяти ценой дополнительных вычислений.
Поэтому выбор зависит от конкретной задачи, что вы там будете хранить и в каких объёмах. Если не так много данных, то первый вариант предпочтительнее второго.
 
Сверху Снизу