4

概要: PredicateBuilderを使用してOr()いくつかの式をまとめてから、その組み合わせた式をOrmLiteSelect()メソッドに送信しています。ただし、生成された SQL には、WHERESQL Server がエラーをスローする、入れ子になった括弧が非常に多い句があります。これを回避するにはどうすればよいですか?

詳細:Foo 2 つの列を持つテーブルがBarありBazます。Bar/Baz 値のコレクションがあり、一致するすべての行を見つけたい場合は、(たとえば) 次の SQL を発行します。

SELECT * FROM Foo WHERE (Bar=1 AND Baz=1) OR (Bar=2 AND Baz=3) OR ...

私はOrmLiteを使用PredicateBuilderしているので、where 句を生成するために使用しています。

var predicate = PredicateBuilder.False<Foo>();
foreach (var nextFoo in fooList)
    predicate = predicate.Or(foo => nextFoo.Bar == foo.Bar && 
                                    nextFoo.Baz == foo.Baz);
Db.Select(predicate);

リストに 3 つの Foo を指定してこれを実行すると、生成された SQL は次のようになります (簡潔にするためにクリーンアップされていますが、意図的に 1 行に残しています)。

SELECT Bar, Baz FROM Foo WHERE ((((1=0) OR ((1=Bar) AND (1=Baz))) OR ((2=Bar) AND (3=Baz))) OR ((2=Bar) AND (7=Baz)))

先頭の括弧に注意してください。はPredicateBuilder、次の式を追加する前に既存の式を継続的に括弧で囲んで、x-> (x) or y->((x) or y) or zなどとします。

私の問題:検索するアイテムが数十または数百ある場合、生成された SQL には数十または数百のネストされた括弧があり、SQL Server はそれを : でキックバックしますSqlException

SQL ステートメントの一部のネストが深すぎます。クエリを書き直すか、小さなクエリに分割します。

それで、これについて何ができますか?ネスト例外を回避したい場合は、生成された SQL のWHERE句を (上記の例のクエリのように) フラット化する必要があります。独自の SQL を動的に生成して OrmLite のSqlListメソッドに送信できることはわかっていますが、それを強制されると、OrmLite の価値の半分が失われます。

4

1 に答える 1

7

SQL は s をショートサーキットしないのでOR、次のような式ツリーを変換できます。

OR
 \
 OR
  \
  OR
   \
   OR

次のような式ツリーに:

        OR
      /    \
     /      \
    /        \
   OR        OR
 /   \     /    \
OR   OR   OR    OR

これは単なる回避策です。理想的には、フレームワークはそのような状況に対処できる必要があります。

このようなツリーを構築する 1 つの方法は、リストを再帰的に半分に分割し、ORそれぞれの半分から「-ツリー」を再帰的に構築し、2 つの「OR-ツリー」を別のものと結合することORです。

Predicate ToOrTree(List<Foo> fooList) {
    if (fooList.Count > 2) {
        var firstHalf = fooList.Count / 2;
        var lhs = ToOrTree(fooList.Take(firstHalf).ToList());
        var rhs = ToOrTree(fooList.Skip(firstHalf).ToList());
        return lhs.Or(rhs);
    }
    Predicate res = PredicateBuilder.Create<Foo>(
        foo => fooList[0].Bar == foo.Bar &&  fooList[0].Baz == foo.Baz
    );
    if (fooList.Count == 2) {
        res = res.Or(
            foo => fooList[1].Bar == foo.Bar &&  fooList[1].Baz == foo.Baz
        );
    }
    return res;
}
于 2013-07-19T20:17:00.723 に答える