ActiveRecord вложенный SELECT - могу ли я сделать это без ручного SQL? - PullRequest
2 голосов
/ 04 декабря 2009

У меня есть таблица с (среди прочего) именем и рангом. Я хотел бы вернуть набор всех уникальных имен, но для каждого возвращенного имени я хотел бы выбрать строку с самым высоким рангом. Это просто с двумя вложенными инструкциями SELECT:

SELECT * FROM (SELECT * FROM foo ORDER BY rank DESC) AS ordered GROUP BY name

MySQL получает первое "совпадение" для каждого имени, которое (из-за более раннего ORDER BY) всегда будет самым высоким.

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

result = foo.all
result.delete_if do |item|
  isOutranked = false
  result.each do |row|
    if (row.name == item.name) and (row.rank > item.rank) then isOutranked = true
  end
  isOutranked
end

Я думаю, что работает , но все равно кажется, что должен быть лучший путь. Решение этой проблемы с помощью хитрости ActiveRecord или более элегантной манипуляции с массивами приветствуется!

1 Ответ

2 голосов
/ 04 декабря 2009

MySQL получает первое "совпадение" для каждого имени, которое (из-за более раннего ORDER BY) всегда будет самым высоким.

Запрос, который вы используете для возврата верхней строки в группе, не гарантирован. Это всего лишь совпадение реализации, и она может быть изменена. Не полагайтесь на это.

Вы пытаетесь решить проблему " great-n-per-group ", которую я часто вижу в StackOverflow. Вот запрос, который дает ответ более надежно:

SELECT t1.*
FROM foo AS t1
LEFT OUTER JOIN foo AS t2
 ON (t1.name = t2.name AND t1.rank < t2.rank)
WHERE t2.name IS NULL;

Альтернатива, которая делает то же самое:

SELECT *
FROM foo AS t1
WHERE NOT EXISTS 
    (SELECT * FROM foo AS t2
     WHERE t1.name = t2.name AND t1.rank < t2.rank);

Я мог бы просто выбросить вышесказанное в find_by_sql, но это просто грязно.

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

...