Rails 2.3.8: как выбрать записи на основе наибольшего значения поля? - PullRequest
0 голосов
/ 23 ноября 2010

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

Я ответил на этот вопрос. Но это не правильно.

Записи в таблице

alt text

Revision.find(:all,:select => "id,name,max(revision) as revision", :conditions => ["saved=1"],:group => 'name')

, что приведет к

alt text

На самом деле результат должен быть id 3,6, 8.

какая модификация в запросе приведет к такому результату?

Ответы [ 4 ]

1 голос
/ 23 ноября 2010

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

Существует несколько способов решения этой проблемы, специфичных для SQL, но ни один из них не очень хорошо подходит для Rails. Вместо этого я бы структурировал ваши данные так:

  1. Имейте модель recipe_revisions, которая включает полную запись рецепта
  2. Имейте модель рецептов, которая в основном просто id / name / последней редакции.
  3. Чтобы получить текущую копию рецепта, вы выбираете нужный рецепт из рецептов, а затем присоединяетесь к recipe_revisions по имени / максимальной ревизии, где эти столбцы индексируются в обеих таблицах.

Это не простое решение, оно будет хорошо работать.

0 голосов
/ 02 июня 2015

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

select id, name, saved, revision
from revisions r
where saved = 1 and
version_no = (select revision 
              from revisions r2
              where saved = 1 and r2.name = r.name
              order by r2.revision desc
              limit 1 
             )

Предупреждение: я не уверен в влиянии на производительность, хотя, как я подозреваю, это делает второй подзапрос для каждого уникального поля ревизии.

0 голосов
/ 23 ноября 2010
Revision.find(:all, :select => "DISTINCT id", :conditions => ["saved=1"], :order => 'revision DESC')
0 голосов
/ 23 ноября 2010

Разделите ваш запрос на две части.В первом найдите максимальную ревизию для каждого имени.Во втором запросе найдите полную строку, которая соответствует имени и ревизии.

CREATE TEMP TABLE max_revisions (name varchar, revision integer);
INSERT INTO max_revisions SELECT name, max(revision) FROM revisions
       WHERE saved = 1 GROUP BY name;
SELECT r.* FROM revisions r INNER JOIN max_revisions m
       ON m.name = r.name AND m.revision = r.revision;

Теперь ваша проблема может состоять в том, как выразить это в Rails.

Вы можете использовать Revision.connection.executeа затем Revision.find_by_sql (оба в одном методе, например: Revision.find_by_max_revisions).

В противном случае, если ваша база данных не поддерживает временные таблицы или вы просто не хотите их использовать, вы можете прочитатьmax_revisions в память, а затем использовать его для построения запроса:

class Revision
  def self.find_by_max_revisions
    max_revs = connection.select_values(
      sanitize_sql(
        ["SELECT name, max(revision) as max_rev FROM #{table_name}
               WHERE saved = ? GROUP BY name", 1]
      ), "Load max_revisions for #{self.name}"
    )
    max_revs.map do |row|
      find_by_name_and_revision(row["name"], row["max_rev"].to_i)
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...