Лучшая нормализация базы данных

0

У меня эти таблицы в моей базе данных:

bookings(id,lessons_id,users_id)
lessons(id,name,date,max_enrollments,enrollments)
permits(id,user_id,used_entries,entries)

Каждый раз, когда я вставляю новую запись при бронировании

lessons.enrollments increments by +1 
permits.used_entries -1 where user_id eq Auth()->id
permits.entries increments by +1

Я уже сделал три funx, которые делают это в моем PHP-коде, но prb должен сделать это автоматически! Может кто-нибудь дать мне несколько предложений?

  • 0
    Вы должны быть в состоянии сделать это с помощью триггеров.
  • 0
    @TimBiegeleisen дай мне маленький пример
Показать ещё 2 комментария
Теги:
database

1 ответ

1
Лучший ответ

Я не уверен, насколько далеко вы находитесь в разработке приложений или насколько легко изменить свою схему, но если это возможно, вы должны попытаться получить вашу базу данных в нормальном виде. Удобный совет, чтобы получить базу данных в нормальном виде, - это помнить это высказывание:

Я торжественно клянусь представить факт о ключе, ключе, и ничего, кроме ключа - так помогите мне, Кодд

  • Предоставьте факт о ключе: это означает, что каждое поле в вашей базе данных должно предоставлять ОДИН факт. Вместо поля "номера телефонов" (если у пользователя может быть несколько телефонных номеров) у вас должна быть таблица "телефонные номера" с "user_id" и "номер телефона" (единственная). Таким образом, вы никогда не храните два факта (два телефонных номера) в одном поле

  • Весь ключ: это гарантирует, что ваша база данных не имеет сгруппированных или дублированных данных. Предположим, у вас есть таблица изображений, и для каждого изображения вы храните имя изображения (например, "котята"), тип изображения (например, "jpeg") и поддерживает ли он прозрачность (например, "ложь"). В этом случае, чтобы однозначно идентифицировать одно изображение, вам нужно знать его имя и его тип. "kittens.jpg" отличается от "kittens.png". Однако независимо от того, поддерживает ли изображение прозрачность, его тип может быть определен (PNG поддерживает прозрачность, JPEG не), но НЕ зависит от его имени. Это означает, что каждый JPEG в вашей таблице будет иметь это значение, равное false... надеюсь. Но что произойдет, если один JPEG устанавливает это в true? Вы доверяете этой строке базы данных? Чтобы решить эту проблему, вы создаете отдельную таблицу "типы изображений", тогда таблица изображений будет просто ссылаться на один из типов в таблице "типы изображений".

  • И ничего, кроме ключа: любое поле в таблице должно зависеть только от ключа, а не от чего-либо еще. Что-то вроде "зачислений, оставшихся" будет зависеть от количества "максимальных зачислений". Подобно тому, как предыдущее правило предотвращало недопустимые недостоверные данные по нескольким строкам (например, один JPEG говорил, что он поддерживает прозрачность, а другой говорит, что это не так), это правило предотвращает ошибочные недопустимые данные в одной строке. Если у вас осталось 5 учащихся, но не более 3 макс, это не имеет смысла!

Судя по тому, как вы изменяете поля при создании бронирования, я предполагаю, что ваша логика выглядит примерно так:

  • Ваше приложение состоит из "пользователей" и "уроков",
  • "Пользователь" получает определенное количество "разрешений",
  • Чтобы подписаться на "урок", пользователь должен расходовать одно из своих "разрешений",
  • Пользователь не может подписаться на "урок", если урок уже достигнут, максимальное количество пользователей
  • Несколько пользователей могут зарегистрироваться для одного и того же урока, и пользователь может подписаться на несколько уроков (при условии, что у него достаточно разрешений)

В этом случае количество разрешений, которое пользователь имеет, является фактом о пользователе. Поэтому я бы изменил вашу схему так:

users(id, name, permits)
lessons(id, name, date, max_users)
bookings(user_id, lesson_id)

Это минимальная схема, описывающая описанную выше систему. Теперь в вашем коде, когда пользователь пытается сделать заказ, вы должны проверить две вещи:

(count(bookings) where lesson_id = lesson.id) < lesson.max_users
(count(bookings) where user_id = user.id) < user.permits

Если вы оба проверите, вы разрешите бронирование пройти. Когда вы создаете заказ , все, что вам нужно сделать. Поскольку база данных находится в нормальной форме, вам не нужно редактировать какие-либо поля или обновлять любые значения. Вы просто создаете заказ, и все готово!

Теперь короткая заметка о производительности: 99% времени, нормальная форма - правильный путь. Однако нормальная форма часто торгует производительностью для целостности данных. Если у вас есть миллионы уроков и миллионы пользователей, вы можете обнаружить, что выполняется count(bookings) where lesson_id = lesson.id занимает слишком много времени. В этом случае вам может потребоваться денормализация ваших данных.

Денормализация позволяет повысить производительность за счет чего приложение путь более сложным. Это всегда должно быть абсолютным последним, что вы делаете для повышения производительности, потому что сложность приложения редко стоит того. Прежде чем вы денормализовать данные, убедитесь, что вы сделали все, что в ваших силах, чтобы максимально использовать индексирование, кеширование и т.д.

Обновление: запрос для удовлетворения ваших потребностей

Вы запросили запрос:

получить все уроки, когда этот пользователь [ввел]

Возможно, я неправильно понял этот первоначальный запрос, так как позже вы сказали, что хотите все уроки, которые не были забронированы для пользователя. Вы также разместили код, который выглядит так, будто вы используете Laravel. Я постараюсь собрать для вас два куска кода Laravel - по одному для каждого из этих вопросов:

// All lessons the user *has* booked
$lessons = DB::table("Bookings")
               ->join("Lessons", "Bookings.lesson_id", "=", "Lessons.id")
               ->where("Bookings.user_id", "=", Auth::user()->id);

// All lessons the user *has not* booked
$lessons = DB::table("Lessons")
               ->whereNotExists(function ($query) {
                   $query->select(DB::raw(1))
                         ->from("Bookings")
                         ->where("user_id", "=", Auth::user()->id);
               });

Хотя я считаю, что это сработает, вам может быть легче воспользоваться встроенными отношениями Laravel.

class User extends Eloquent {
    public function lessons() {
        return $this->belongsToMany("App\Lesson", "Bookings");
    }
}
class Lesson extends Eloquent {
    public function users() {
        return $this->belongsToMany("App\User", "Bookings");
    }
}

Учитывая эти отношения, вы можете упростить оба запроса:

$lessons = \App\Lesson::whereHas("users", function($query) {
    $query->where("user_id", "=", Auth::user()->id);
});
$lessons = \App\Lesson::whereDoesntHave("users", function($query) {
    $query->where("user_id", "=", Auth::user()->id);
});

Отказ от ответственности: я не тестировал ни один из вышеуказанных запросов, но если ничего больше, они, надеюсь, указывают вам в правильном направлении.

  • 0
    +10 приятель только за время, затраченное на написание всего этого, супер
  • 0
    И последний вопрос: как я могу получить все уроки, в которые этот пользователь вошел, я сделал что-то подобное, но, по-видимому, это неверный code SELECT lessons.* from lessons left join bookings on lessons.id =bookings.lessons_id WHERE lessons.date > NOW() and bookings.user_id <> 2
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню