- Загрязненные базы данных снижают производительность.
- Решение наивно простое: не вводите в наш набор данных противоречивые записи.
- Давайте рассмотрим, как поддерживать стабильную и чистую базу данных на практических примерах.
- 1. Определение полезных параметров
- Цепочка методов
- 2. Уникальная идентификация записей
- 3. Обработка ответов на запросы
- В заключение
- Ресурсы
Загрязненные базы данных снижают производительность.
В машинном обучении наборы данных, загрязненные дубликатами, заставляют модели изучать искусственные закономерности, которых не существует. В реальном контексте центров обработки вызовов экстренных служб множество звонков по одной и той же жалобе заставляют нескольких сотрудников реагировать на один и тот же инцидент.
Следовательно, неправильное распределение ресурсов в результате плохой структуры набора данных создает более серьезные проблемы, чем первоначально предполагалось.
Решение наивно простое: не вводите в наш набор данных противоречивые записи.
Хотя ограничения и параметры каждого набора данных различны, общий процесс и применение этого решения одинаковы.
- Определите, какие параметры будут использоваться для поиска существующих записей в наборе данных вашей модели.
- Уникально идентифицируйте каждую запись при отправке записи/записей.
- Обработать все ответы на запросы к набору данных.
Достаточно просто, верно? К счастью, ActiveRecord предлагает несколько методов поиска. Эти методы позволяют нам определить, существует ли конфликтующая запись, прежде чем вводить новую запись в нашу базу данных.
Давайте рассмотрим, как поддерживать стабильную и чистую базу данных на практических примерах.
Queue Wag n’ Walk — планировщик расписания, предназначенный для выгула собак. Есть несколько различных головных болей, которые могут возникнуть из-за загрязнения базы данных; давайте определим различные записи, которые могут повредить нашу таблицу встреч.
Этот пример требует базового понимания гема ActiveRecord, ассоциаций таблиц и таблиц SQL.
1. Определение полезных параметров
Каждая запланированная встреча имеет 4 различных пользовательских входа: дата/время, собака, выгульщик (сотрудник) и продолжительность прогулки. Следует выделить несколько потенциальных конфликтов при планировании:
- Назначение выгульщика на время, которое противоречит его текущему расписанию.
- Запланировать собаку на время, которое противоречит текущему расписанию.
- назначение одной и той же встречи дважды
Первый метод, который следует рассмотреть, это метод find_or_create_by.
Этот метод либо находит первую запись с заданными атрибутами, либо создает новую запись, если таковая не найдена. Этот метод полезен, когда мы ищем точную запись, или когда наш набор данных прост.
Appointment.find_or_create_by({
employee_id: params[:employee_id] ,
start: params[:start]})
Если в нашей таблице назначений не найдена запись с указанным идентификатором сотрудника и датой/временем начала приема, то вводится новая запись.
Если мы попытаемся запланировать прием, в то время как существующий прием находится в процессе, наш метод find_or_create_by не заметит этого и внесет в расписание конфликтующую запись для нашего ходока.
Цепочка методов
Второй метод, который следует рассмотреть, — это комбинация нескольких методов, известная как цепочка методов. С помощью цепочки методов мы можем применять несколько условий для проверки и поиска записей в нашей базе данных.
.где Метод
exist = Appointment.where({
start: params[:start] ,
dog_id: params[:dog_id]
})
Метод .where выбирает все записи, которые соответствуют атрибутам, переданным в метод. В данном случае exist будет равен всем встречам, время начала которых совпадает с указанным временем начала, а идентификатор собаки совпадает с идентификатором собаки.
.или Метод
exist = Appointment.where({
start: params[:start] ,
dog_id: params[:dog_id]
}).or(Appointment.where({start: params[:start] ,
employee_id: params[:employee_id]}))
Используйте метод .or, чтобы добавить второе условие к запросу. В данном случае мы ищем любую встречу, соответствующую указанному времени начала и идентификатору собаки, или любую встречу, соответствующую указанному времени начала и идентификатору сотрудника.
Метод .find_all
appt_in_progress_dogs = Appointment.all
.find_all {|a| a[:dog_id] == params[:dog_id]}
Метод find_all возвращает массив, содержащий выбранные записи, для которых заданный блок возвращает истинное значение. В данном случае мы находим все записи из таблицы Appointments, чей идентификатор собаки совпадает с указанным идентификатором собаки.
.between? Метод
appt_in_progress_dogs = Appointment.all
.find_all {|a| a[:dog_id] == params[:dog_id]}
.find_all {|a| params[:start].between?(a[:start] , a[:end])}
Метод between возвращает значение true или false. Он определяет, находится ли значение между заданным минимумом или максимумом. В данном случае мы хотим найти все записи с идентификатором собаки, совпадающим с заданным идентификатором собаки. Затем из этих записей мы хотим найти все случаи, когда новая встреча начинается в то время, когда существующая встреча для данной собаки находится в процессе.
Другими словами, мы не хотим назначать прогулку для собаки, если она находится в середине прогулки. Тот же случай применим для любого выгульщика (сотрудника).
Метод .find_in_batches
Appointment.find_in_batches(batch_size: 1000) do |a|
a.find_by({start: params[:start]})
end
Метод find_in_batches полезен при работе с большими наборами данных. Если указать размер партии, то этот метод будет запрашивать только указанный размер партии за один раз. Этот метод снижает некоторые проблемы производительности, возникающие при работе с большими наборами данных.
Мы можем быть более целенаправленными и конкретными в отношении типов записей, которые мы хотим найти с помощью цепочки методов.
2. Уникальная идентификация записей
Этот ответ довольно прост при использовании Active Record. При использовании Active Record Migrations столбец первичного ключа создается автоматически, и каждой записи, внесенной в нашу таблицу, присваивается уникальный идентификатор.
3. Обработка ответов на запросы
Теперь, когда мы сделали запрос к таблице назначений, мы будем условно отвечать на запрос пользователя о новой встрече.
Утверждение IF
if !exist.exists? &&[*appt_in_progress_dogs,*appt_in_progress_walkers].length < 1
Оператор IF в нашем методе POST использует запросы, которые мы выполнили. Если новый запрос на прием точно не совпадает ни с одной существующей записью, то он проходит первое условие в операторе IF. Метод .exists? возвращает true или false. В этом случае, если записи не существует, мы переходим к проверке второго условия.
Второе условие оператора IF использует оператор splat (*), который функционирует аналогично оператору spread (…) в Javascript (ES6). Если для указанной собаки или выгульщика не найдено ни одной незавершенной встречи, то длина нашего массива будет равна нулю.
Если оба условия выполнены, то будет создано новое назначение с предоставленными атрибутами, отправленными нашим фронт-эндом, а наш ответ будет отправлен в виде объекта JSON.
Утверждение Else
Если одно из условий возвращает false, то наш бэкэнд отправит ответ с сообщением об ошибке в виде объекта JSON. Некоторые ошибки могут быть сложнее, чем другие, и поэтому вы можете настроить несколько условий для разных ошибок.
fetch(`http://localhost:3005/appointments` , {
method : 'POST' ,
headers: { "Content-Type" : 'application/json'} ,
body : JSON.stringify(newAppointment)
})
.then(r => r.json())
.then((appointment) => {
if(Object.keys(appointment).length === 1) {
alert(appointment.error)
} else {
setAppointments([...appointments , appointment])
alert(`Appointment for ${newDate} at ${time} has been scheduled`)
}
})
Учитывая ответ, полученный от нашего первоначального POST-запроса, мы оповещаем пользователя либо об успешно назначенной встрече, либо об ошибке, которую мы отправили из бэкенда.
Те же запросы и условия могут быть применены и к запросу PATCH. Нам также нужно проверить, вызовет ли обновление существующей встречи такое же загрязнение, как и наш POST-запрос.
В заключение
ActiveRecord предоставляет множество полезных методов для запросов к нашим базам данных. Цепочки методов позволяют нам быть более целенаправленными и конкретными в наших запросах. Определите, существует ли конфликтующая запись в нашей базе данных, и условно ответьте на возможные результаты.
Ресурсы
Как работать с дублирующимися записями
CS: Дублирующиеся записи
Что такое очистка данных
Документы по Ruby on Rails
Документы по миграции активных записей