4

摘要:我将PredicateBuilder用于Or()几个表达式,然后将该组合表达式发送到OrmLiteSelect()方法。但是,生成的 SQL 有一个WHERE带有太多嵌套括号的子句,以至于 SQL Server 会引发错误。我能做些什么来解决这个问题?

详细信息:我有一个Foo有两列的表,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);

如果我在 list 中使用 3 Foos执行此操作,生成的 SQL 将如下所示(为简洁起见进行了清理,但有意保留在一行以表明观点):

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 语句的某些部分嵌套得太深。重写查询或将其分解为更小的查询。

那么我能做些什么呢?WHERE如果我想避免嵌套异常,我需要将生成的 SQL子句展平(如上面的示例查询)。我知道我可以动态生成自己的 SQL 并将其发送到 OrmLite 的SqlList方法,但被迫这样做会破坏 OrmLite 的一半价值。

4

1 回答 1

7

由于 SQL 不会短路ORs,因此您可以转换如下所示的表达式树

OR
 \
 OR
  \
  OR
   \
   OR

到如下所示的表达式树:

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

这只是一种解决方法:理想情况下,框架应该能够处理这样的情况。

构造这样一棵树的一种方法是将列表递归地分成两半,OR从每一半递归地构造一个“-tree”,然后将两个“ OR-tree”与另一个合并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 回答