1

すべてではありませんが一部のレコードに「パス」フィールドがあり、すべてに「値」フィールドがあります。パスを持たないレコード、または特定のパスを持つすべてのレコードの中で最大の値を持つレコードのみを選択したいと考えています。

つまり、次のレコードが与えられます。

Name:  Path:   Value:
A      foo     5
B      foo     6
C      NULL    2
D      bar     2
E      NULL    4

B、C、D、および E を返したいのですが、A は返しません (A にはパスがあり、そのパスは B と同じですが、A の値の方が小さいため)。

ActiveRecord、ARel、および Postgres を使用してこれを達成するにはどうすればよいですか? 理想的には、スコープとして機能するソリューションが欲しいです。

4

3 に答える 3

2

2つのサブクエリを使用してこのようなものを使用できます(サブクエリを持つSQLクエリを1つだけ実行します)。テストしませんでしたが、正しい方向に進むはずです。これはPostgres用です。

scope :null_ids, -> { where(path: nil).select('id') }
scope :non_null_ids, -> { where('path IS NOT NULL').select('DISTINCT ON (path) id').order('path, value desc, id') }
scope :stuff, -> {
  subquery = [null_ids, non_null_ids].map{|q| "(#{q.to_sql})"}.join(' UNION ')
  where("#{table_name}.id IN (#{subquery})")
}

別のDBを使用している場合は、non_nullsスコープに対してdistinct onではなく、group/orderを使用する必要がある場合があります。クエリの実行が遅い場合は、パスと値にインデックスを付けます。

取得できるクエリは1つだけで、それは連鎖可能なスコープです。

于 2013-03-03T13:43:09.947 に答える
1

説明をSQLに簡単に音訳すると、次のようになります。

select name, path, value
from (
    select name, path, value,
           row_number() over (partition by path order by value desc) as r
    from your_table
    where path is not null
) as dt
where r = 1
union all
select name, path, value
from your_table
where path is null

それをaでラップして、find_by_sqlオブジェクトを反対側に出すことができます。

そのクエリは次のように機能します。

  1. row_number ウィンドウ関数を使用すると、行をでグループ化し、path各グループをで並べ替えてから、各グループの行に番号を付けることができますvalue。SQLを少しpsql試してみると、これがどのように機能するかがわかります。他にも、あらゆる種類のすばらしいことを実行できるウィンドウ関数があります。
  2. pathNULL値をNULL以外の値とは別に扱っているpathためpath is not null、内部クエリで。
  3. path派生テーブルから行番号が1(つまり)の行を選択することで、各グループの最初の行をはがすことができますwhere r = 1
  4. 行の処理はpath is null、セクションクエリによって簡単に処理されます。
  5. UNIONは、クエリの結果セットを結合するために使用されます。

ActiveRecordを使用してそのようなクエリを構築する方法も、ActiveRecordのスコープメカニズムとそのようなクエリを統合する方法も考えられません。のWHEREコンポーネントだけに簡単にアクセスできる場合は、スコープチェーンのWHEREコンポーネントを使用して、そのクエリのコンポーネントとコンポーネントをActiveRecord::Relation拡張できます。でもどうやってやるのかわからない。where path is not nullwhere path is null

実のところ、私は一瞬でActiveRecordを放棄する傾向があります。ActiveRecordは、私が行う複雑なことのほとんどにとってかなり面倒であり、SQLほど表現力がありません。これは私が今まで使用したすべてのORMに当てはまるので、問題はActiveRecordに固有のものではありません。

于 2013-03-03T06:38:59.290 に答える
0

私は ActiveRecord の経験はありませんが、SQLAlchemy を使用したサンプルを使用して、SQL だけを使用する群衆を沈黙させます ;)

q1 = Session.query(Record).filter(Record.path != None)
q1 = q1.distinct(Record.path).order_by(Record.path, Record.value.desc())

q2 = Session.query(Record).filter(Record.path == None)

query = q1.from_self().union(q2)
# Further chaining, e.g. query = query.filter(Record.value > 3) to return B, E

for record in query:
    print record.name
于 2013-03-03T08:49:17.040 に答える