#sql #ruby-on-rails #ruby #postgresql
#sql #ruby-на-рельсах #рубин #postgresql #ruby-on-rails #ruby
Вопрос:
Я создаю необработанные SQL-запросы в свою базу данных PG в Rails. Когда я использую только одно условие даты в предложении WHERE, работает отлично, но когда я использую 2 условия даты, это работает не так, как ожидалось. Чтобы просмотреть результаты, я выполняю для каждого из них через объект results и печатаю его на консоли.
Только с одним условием:
connection = ActiveRecord::Base.connection()
results = connection.execute("SELECT *
FROM training_employees
LEFT JOIN trainings
ON training_employees.training_id = trainings.id
WHERE trainings.date >='2014-06-29'")
results.each do |row|
puts row
end
ВОЗВРАТ
{"id"=>"1", "employee_id"=>"2", "training_id"=>"1",
"created_at"=>"2014-06-29 06:44:13.261074",
"updated_at"=>"2014-06-29 06:44:13.261074",
"name"=>"Incendios", "date"=>"2014-06-30 02:43:00",
"expected_attendance"=>"20", "service_company_id"=>"1"}
{"id"=>"1", "employee_id"=>"3", "training_id"=>"1",
"created_at"=>"2014-06-29 06:44:13.261074",
"updated_at"=>"2014-06-29 06:44:13.261074",
"name"=>"Incendios",
"date"=>"2014-06-30 02:43:00",
"expected_attendance"=>"20", "service_company_id"=>"1"}
{"id"=>"1", "employee_id"=>"7", "training_id"=>"1",
"created_at"=>"2014-06-29 06:44:13.261074",
"updated_at"=>"2014-06-29 06:44:13.261074",
"name"=>"Incendios", "date"=>"2014-06-30 02:43:00",
"expected_attendance"=>"20", "service_company_id"=>"1"}
{"id"=>"1", "employee_id"=>"9", "training_id"=>"1",
"created_at"=>"2014-06-29 06:44:13.261074",
"updated_at"=>"2014-06-29 06:44:13.261074",
"name"=>"Incendios", "date"=>"2014-06-30 02:43:00",
"expected_attendance"=>"20", "service_company_id"=>"1"}
{"id"=>"1", "employee_id"=>"10", "training_id"=>"1",
"created_at"=>"2014-06-29 06:44:13.261074",
"updated_at"=>"2014-06-29 06:44:13.261074",
"name"=>"Incendios", "date"=>"2014-06-30 02:43:00",
"expected_attendance"=>"20", "service_company_id"=>"1"}
=> #<PG::Result:0x007fa8b9cfc038
@connection=#<PG::Connection:0x007fa8b994fcf0
@socket_io=nil, @notice_receiver=nil,
@notice_processor=nil>>
Когда я выполняю запрос с двумя условиями
results = connection.execute("SELECT *
FROM training_employees
LEFT JOIN trainings
ON training_employees.training_id = trainings.id
WHERE trainings.date >='2014-06-29'
AND trainings.date <='2014-06-30'")
results.each do |row|
puts row
end
Возвращайте только это, а не список записей, как при одном условии
=> #<PG::Result:0x007fa8b75c5398
@connection=#<PG::Connection:0x007fa8b994fcf0 @socket_io=nil,
@notice_receiver=nil, @notice_processor=nil>>
Я протестировал условия по отдельности и возвращаю то, что я ожидаю (существующие записи между диапазоном дат)
Это мои модели
Training(id: integer, name: string, date: datetime,
expected_attendance: integer, created_at: datetime,
updated_at: datetime, service_company_id: integer)
TrainingEmployee(id: integer, employee_id: integer,
training_id: integer, created_at:
datetime, updated_at: datetime)
Я использую ruby 2.1.1 и Rails 4.1.0.
Ответ №1:
Ваша проблема в том, что на самом деле вы получаете только полночь 30-го числа. Когда ваши типы будут преобразованы для сравнения, вот как на самом деле выглядит ваш запрос:
SELECT *
FROM training_employees
LEFT JOIN trainings
ON training_employees.training_id = trainings.id
WHERE trainings.date >='2014-06-29 00:00:00.000000'
AND trainings.date <='2014-06-30 00:00:00.000000'
…. поэтому, конечно, никакие записи из trainings
with date = "2014-06-30 02:43:00"
не будут включены. Правильный способ запроса даты / времени / временных меток — использовать эксклюзивную верхнюю границу, то есть trainings.date < '2014-07-01'
.
Это также приводит к тому, что записи вообще не возвращаются, потому что на самом деле у вас нет LEFT JOIN
, фактически у вас есть INNER JOIN
. Вот почему; когда WHERE
предложение выполняется, все, что является LEFT JOIN
отредактированным и не имеет записей, будет выполнять эти условия null
и заставит базу данных исключить их. Если вы действительно хотите LEFT JOIN
, вам нужно переместить условия в ON
предложение. Если вы действительно хотите INNER JOIN
, вам все равно следует переместить их в ON
предложение, поскольку это делает соединение более очевидным.
В сторону: что trainings.date
на самом деле представляет? Это ужасно двусмысленное название…
Комментарии:
1. тренинги. дата представляет дату, когда предлагается обучение. Спасибо!
2. @rcrivera — тогда оно должно быть названо как
offered_On
, илиoffered_At
(это более вероятно, если включена информация о времени). Как правило, старайтесь не указывать тип в именах переменных / столбцов. (Есть несколько вещей, которые, кажется , включают эту информацию, но не совсем, например,calendar_date
илиbusiness_date
, где «дата» — это понятие, которого было бы неудобно избегать).
Ответ №2:
Все временные метки в вашем date
на самом деле больше, чем 2014-06-30, потому что все пять из них:
"date"=>"2014-06-30 02:43:00"
Дата 2014-06-30
будет соответствовать временной метке 2014-06-30 00:00:00
, поэтому компоненты времени суток ваших временных меток вмешиваются в логику вашего запроса.
Решение состоит в том, чтобы использовать date
столбец для хранения дат, а не временную метку. Тогда у вас не будет никаких проблем с часовым поясом, временем суток ….