Я изо всех сил пытаюсь понять запрос ActiveRecord.
Я пытаюсь найти в своей базе данных объекты GolfRetailer с идентификаторами 1..100, у которых есть что-то (не nil) в их поле :website, и у которых нет true в их поле duplicate_domain.
Вот запрос, который я ожидал сработать:
GolfRetailer.where.not(website: nil, duplicate_domain: true).where(id: 1..100)
Я также пробовал этот вариант по сути того же запроса: GolfRetailer.where.not(website: nil).where(id: 1..100, duplicate_domain: !true)
Но оба возвращают пустой массив, несмотря на то, что определенно есть записи, соответствующие этим требованиям.
Когда я запускаю GolfRetailer.where.not(website: nil).where(id: 1..100), я получаю массив, а когда я запускаю GolfRetailer.where.not(website: nil, duplicate_domain: nil).where(id: 1..100), я также получаю массив, но со всеми записями, которые делать имеют истинный флаг повторяющегося_домена, а это не то, что я ищу.
Я бы предпочел не искать записи с duplicate_domain: nil, так как это не всегда правильно (возможно, я еще не обработал их домен).
Для ясности, вот схема для модели.
create_table "golf_retailers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "place_id"
t.string "website"
t.string "formatted_address"
t.string "google_places_name"
t.string "email"
t.boolean "duplicate_domain"
t.index ["duplicate_domain"], name: "index_golf_retailers_on_duplicate_domain"
end
Чего мне не хватает, чтобы этот запрос работал?
Я настоятельно рекомендую переключиться на t.boolean "duplicate_domain", null: false для столбца, null в логическом столбце редко то, что вам нужно. Кроме того, индексация логического столбца (или любого столбца с низкой кардинальностью), вероятно, не поможет, проверьте выходные данные EXPLAIN в ваших запросах, чтобы увидеть, используется ли когда-либо этот индекс.
Эти комментарии помогли мне лучше понять хороший дизайн. Спасибо, что нашли время, чтобы объяснить это.





Это происходит потому, что в SQL, когда вы делаете != TRUE, любые значения NULL не будут включены в результат. Это связано с тем, что значение NULL представляет собой неизвестное значение, поэтому БД не знает, как выполнить какие-либо операции сравнения с неизвестным значением, и поэтому они исключены.
Один из способов обойти это — использовать IS DISTINCT FROM:
GolfRetailer
.where(id: 1..100)
.where.not(website: nil)
.where("duplicate_domain IS DISTINCT FROM ?", true)
Как уже упоминали другие, вы также должны спросить себя, действительно ли это так, что вам никогда не известно, есть ли у GolfRetailerduplicate_domain.
Если все GolfRetailer с duplicate_domain из NULL на самом деле означают, что у них нет ни одного (false), то вам следует рассмотреть возможность полного предотвращения значения NULL для этого столбца.
Вы можете сделать это, добавив ограничение NOT NULL для столбца с change_column миграция базы данных.
Чтобы добавить ограничение NOT NULL, вам сначала нужно убедиться, что все данные в столбце имеют ненулевые значения.
def change
GolfRetailer.in_batches.update_all(duplicate_domain: false)
change_column_null :golf_retailers, :duplicate_domain
end
Если ваше приложение находится под нагрузкой, вы также должны быть осторожны с потенциальной производительностью, которую может иметь любая миграция, подобная этой, особенно если вы добавите ограничение NOT NULL со значением по умолчанию.
Рассмотрите возможность использования чего-то вроде драгоценного камня Сильные миграции, чтобы помочь найти миграции БД, которые могут вызвать простои перед производством.
Я бы предложил значение по умолчанию
falseдля атрибутаduplicate_domain. Вы можете сделать это задним числом, добавивGolfRetailer.where(duplicate_domain: nil).update_all(duplicate_domain: false). Как только единственные параметры будут истинными или ложными, первый запрос должен работать нормально. Проблема в том, что сейчас это 3 состояния (истина, ложь или ноль), а не (истина) не равно NULL в SQL.