摘要:我一起使用PredicateBuilder到Or()
几个表达式,然后将该组合表达式发送到OrmLite的Select()
方法。但是,生成的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);
如果我使用 3 Foos in my list 执行此操作,则生成的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
- &gt; (x) or y
- &gt; ((x) or y) or z
等等。
我的问题:当我要查找数十个或数百个项目时,生成的SQL会有数十个或数百个嵌套括号,而SQL Server会使用SqlException
将其踢回:
SQL语句的某些部分嵌套太深。重写查询或将其分解为较小的查询。
那我该怎么办呢?如果我想避免嵌套异常,我需要将生成的SQL的WHERE
子句展平(如上面的示例查询)。我知道我可以动态生成自己的SQL并将其发送到OrmLite的SqlList
方法,但被迫这样做会使OrmLite的值减半。
答案 0 :(得分:6)
由于SQL不会使OR
短路,您可以转换一个看起来像这样的表达式树
OR
\
OR
\
OR
\
OR
到一个如下所示的表达式树:
OR
/ \
/ \
/ \
OR OR
/ \ / \
OR OR OR OR
这只是一种解决方法:理想情况下,框架应该能够处理类似的情况。
构造这样的树的一种方法是将列表递归地分成两半,从递归的每一半构造一个“OR
- 树”,然后将两个“OR
- 树”组合起来s与另一个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;
}