Postgres, Rails и выбор столбцов, которые не входят в групповое предложение - PullRequest
5 голосов
/ 15 марта 2019

У меня есть следующий запрос, в котором я хочу сгруппировать по treatment_selections.treatment_id и выбрать столбец treatments.name для вызова:

@search = Trial.joins(:quality_datum, treatment_selections: :treatment)
.select('DISTINCT ON (treatment_selections.treatment_id) treatment_selections.treatment_id, treatments.name, AVG(quality_data.yield) as yield')
.where("EXTRACT(year from season_year) BETWEEN #{params[:start_year]} AND #{params[:end_year]}")

Я получаю страшную ошибку:

PG::GroupingError: ERROR:  column "treatment_selections.treatment_id" must appear in the GROUP BY clause or be used in an aggregate function

Итак, я переключился на следующий запрос:

@search = Trial.joins(:quality_datum, treatment_selections: :treatment)
.select('treatments.name, treatment_selections.treatment_id, treatments.name, AVG(quality_data.yield) as yield')
.where("EXTRACT(year from season_year) BETWEEN #{params[:start_year]} AND #{params[:end_year]}")  
.group('treatment_selections.treatment_id')

То, что я знаю, не будет работать из-за отсутствия ссылки на treatments.name в предложении group. Но я подумал, что верхний метод должен сработать, так как я ничего не группирую. Я понимаю, что использование таких методов, как AVG и SUM, не требуется для ссылки в предложении группы, но как насчет столбцов, которые не ссылаются на какие-либо агрегатные функции?

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

Вход

SELECT treatment_selections.treatment_id, treatment.name, AVG(quality_data.yield) as yield FROM "trials" INNER JOIN "treatment_selections" ON "treatment_selections"."trial_id" = "trials"."id" INNER JOIN "quality_data" ON "quality_data"."treatment_selection_id" = "treatment_selections"."id" INNER JOIN "treatment_selections" "treatment_selections_trials" ON "treatment_selections_trials"."trial_id" = "trials"."id" INNER JOIN "treatments" ON "treatments"."id" = "treatment_selections_trials"."treatment_id" WHERE (EXTRACT(year from season_year) BETWEEN 2018 AND 2018) GROUP BY treatment_selections.treatment_id)

Ответы [ 2 ]

2 голосов
/ 17 марта 2019

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

@search = Trial.joins(:quality_datum, treatment_selections: :treatment)
.select('treatment_selections.treatment_id, treatments.name, AVG(quality_data.yield) as yield')
.where("EXTRACT(year from season_year) BETWEEN ? AND ?", params[:start_year], params[:end_year])  
.group('treatment_selections.treatment_id, treatments.name')

Хотя это может не сработать для вашего случая использования, если один treatments.id может быть связан с mutiple treatment.name

0 голосов
/ 23 марта 2019

Я не эксперт по Rails, но давайте проанализируем зарегистрированный запрос:

SELECT treatment_selections.treatment_id, treatment.name, AVG (quality_data.yield) в качестве выхода
ОТ "испытаний"
ВНУТРЕННЕЕ СОЕДИНЕНИЕ "treatment_selections" ON "treatment_selections". "Trial_id" = "trials". "Id"
INNER JOIN "quality_data" ON "quality_data". "Treatment_selection_id" = "treatment_selections". "Id"
ВНУТРЕННЕЕ СОЕДИНЕНИЕ "treatment_selections" "treatment_selections_trials" ON "treatment_selections_trials". "Trial_id" = "trials". "Id"
INNER JOIN "процедуры" ON "treatment". "Id" = "treatment_selections_trials". "Treatment_id" * "* WHERE (EXTRACT (год от сезона_ года) между 2018 и 2018 годами)
GROUP BY treatment_selections.treatment_id

Возможно, вы полагаетесь на условие DISTINCT ON, чтобы выполнить эту работу без указания обоих столбцов.Но, как вы видите в журнале, это не переводится в SQL.

SELECT [отсутствует DISTINCT ON (treatment_selections.treatment_id)] ​​* ​​1018 * treatment_selections.treatment_id, treatment.name, AVG (quality_data.yield) в качестве выхода
ОТ "испытаний"
INNER JOIN "treatment_selections" ON "treatment_selections". "Trial_id" = "trials". "Id"
INNER JOIN "quality_data" ON "quality_data". "Treatment_selection_id" = "treatment_selections". "Id"
ВНУТРЕННЕЕ СОЕДИНЕНИЕ "treatment_selections" "treatment_selections_trials" ON "treatment_selections_trials". "Trial_id" = "trials". "Id"
INNER JOIN "процедуры" ON "treatment". "Id" = "treatment_selections_trials". "Treatment_id" ** WHERE (EXTRACT (год от сезона_год) между 2018 и 2018 гг.)
GROUP BY treatment_selections.treatment_id

Но даже если вам удалось заставить Rails реализовать DISTINCT ON ,вы можете не получить ожидаемый результат, потому что DISTINCT ON должен возвращать только одну строку на treatment_id .

Стандартным способом SQLs, чтобы указать оба столбца в качестве группировки в агрегации:

Если treatment_id имеет отношение 1: 1 к treatment_name , то при запускезапрос без функции AVG (и без DISTINCT ON), данные будут выглядеть примерно так:

|   treatment_id    |       name          |  yield    |  
------------------------------------------------------
|        1          |   treatment 1       |    0.50   |
|        1          |   treatment 1       |    0.45   |
|        2          |   treatment 2       |    0.65   |
|        2          |   treatment 2       |    0.66   |
|        3          |   treatment 3       |    0.85   |

Теперь, чтобы использовать функцию усреднения, вы должны агрегировать по(оба) treatment_id и treatment_name .

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

SELECT treatment_selections.treatment_id, обращение s .name, AVG (quality_data.yield) в качестве выхода
ИЗ "испытаний"
ВНУТРЕННИЙПРИСОЕДИНЯЙТЕСЬ к "treatment_selections" ON "treatment_selections". "Trial_id" = "trials". "Id"
INNER JOIN "quality_data" ON "quality_data". "Treatment_selection_id" = "treatment_selections". "Id"
INNER JOIN "treatment_selections "" treatment_selections_trials "ON" treatment_selections_trials "." trial_id "=" trials "." id "
INNER JOIN" процедуры "ON" treatment "." id "=" treatment_selections_trials "." treatment_id "
WHERE (EXTRACT (год от сезона_год) между 2018 и 2018 гг.)
GROUP BY treatment_selections.treatment_id, treatment.name

даст вам следующий результат:

|   treatment_id    |       name          |   AVG(yield)   |  
------------------------------------------------------------
|        1          |   treatment 1       |      0.475     |
|        2          |   treatment 2       |      0.655     |
|        3          |   treatment 3       |      0.85      |

Чтобы понять это лучше, если результирующие данные в первых двух столбцах не были связаны;например:

|   year    |       name          |   yield   |  
-----------------------------------------------
|    2000   |   treatment 1       |    0.1    |
|    2000   |   treatment 1       |    0.2    |
|    2000   |   treatment 2       |    0.3    |
|    2000   |   treatment 3       |    0.4    |
|    2001   |   treatment 2       |    0.5    |
|    2001   |   treatment 3       |    0.6    |
|    2002   |   treatment 3       |    0.7    |

вы все равно должны группировать по год и имя , и в этом случае функция усреднения будет использоваться только тогда, когда год и имято же самое (обратите внимание, что это невозможно сделать иначе), в результате:

|   year    |       name          |   AVG(yield)   |  
---------------------------------------------------
|    2000   |   treatment 1       |     0.15       |
|    2000   |   treatment 2       |     0.3        |
|    2000   |   treatment 3       |     0.4        |
|    2001   |   treatment 2       |     0.5        |
|    2001   |   treatment 3       |     0.6        |
|    2002   |   treatment 3       |     0.7        |
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...