ここに書かれている方法には誤りがあります。正しい方法を別途書きましたので、そちらも合わせて読んで下さい。
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