[Mac][iOS] NSPredicate - 1対多関連のエンティティの検索条件見本

2011年1月11日火曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

2011-01-20 追記
ここに書かれている方法には誤りがあります。正しい方法を別途書きましたので、そちらも合わせて読んで下さい。
Cocoaの日々: [Mac][iOS] NSPredicate - 1対多関連のエンティティの検索条件見本(訂正〜サブクエリーの利用)


お題


こんな1対多のエンティティがあったとする。
発売日(Book.date)が指定日以降の本を書いた著者(Author)の一覧を取得したい。この時の条件(NSPredicate)はどう書くべきか。


条件見本


NSPredicate ではキーパスを指定できるので日付の条件はこう書ける。
books.date <= %@
Author に対してこの条件を投げるわけだが、今回結果として欲しいのは著者の一覧なので該当する Bookが存在する Authorだけが帰ってくるようにしたい。この場合は ANY をつけてやる。
ANY books.date <= %@
Author を対象とした検索条件 NSPredicate はこうなる。
NSPredicate* p = [NSPredicate predicateWithFormat:@"ANY books.date <= %@", date];
実際に発行されているSQL文を確認してみよう。
SELECT DISTINCT 0, t0.Z_PK FROM ZAUTHOR t0
  JOIN ZBOOK t1 ON t0.Z_PK = t1.ZAUTHOR
  WHERE t1.ZDATE >= ?
親子のテーブルをJOINして、条件にマッチしたレコードをDISTINCTを使って重複をなくしている。意図通りだ。SQLのEXISTSを使えばもう少し効率を上げられそうではあるが件数が多くなければこれでも十分。


条件が複数


もう一つ条件を加えて、ある期間内に発売された本を書いた著者の一覧を取得する。単純に条件を加えてみるとこんな感じ。
ANY (books.date >= %@ AND books.date < %@)
これは実はNG。NSInvalidArgumentException (Unsupported predicate) が発生する。正解はこう。
ANY books.date >= %@ AND ANY books.date < %@
ANY をそれぞれの頭につけてやる。

SQLはこうなる。
SELECT DISTINCT 0, t0.Z_PK FROM ZAUTHOR t0
 JOIN ZBOOK t1 ON t0.Z_PK = t1.ZAUTHOR
 JOIN ZBOOK t2 ON t0.Z_PK = t2.ZAUTHOR
 WHERE ( t1.ZDATE >= ? AND  t2.ZDATE < ?)
2回 JOINをしているのがアレだが、まあ意図通りの動きにはなるならない。→ 冒頭に記載された最新情報を参照してください。


備考


範囲条件指定には BETWEEN というのもある。
(例)
NSNumber *one = [NSNumber numberWithInteger:1];
NSNumber *ten = [NSNumber numberWithInteger:10];
NSPredicate *betweenPredicate = [NSPredicate predicateWithFormat:
     @"attributeName BETWEEN %@",
     [NSArray arrayWithObjects:one, ten, nil]];
ただ NSDate を条件にした場合例外が出てしまった。使い方がまずいのか仕様なのか。

.

Responses

Leave a Response

人気の投稿(過去 30日間)