我正在玩一个基本的表达式树优化器来构建查询计划。在解析树时,我可以根据我可以分配给每个操作的权重来决定如何构建它。详尽搜索/生成表达式树的每个组合
如果我有一个简单的树,有2个关于如何执行动作的选择,我希望能够生成树的两个变体,然后可以比较每个的权重以查看哪些是最高效。
例如,下面的代码会允许我来构建表达式树的两个变化加入操作:一个带有MergeJoinExpression
,一个具有NestedLoopJoinExpression
class Customer
{
public int Id { get; set; }
}
class Orders
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
class MergeJoinExpresion : JoinExpression
{
}
class NestLoopJoinExpresion : JoinExpression
{
}
class Visitor : ExpressionVisitor
{
public List<Expression> GetPlans(Expression expr)
{
// ???
}
override VisitJoin(JoinExpression join)
{
// For this join, I can return the following (trite example)
// return MergeJoinExpresion
// return NestLoopJoinExpresion
return base.VisitJoin(join);
}
}
我如何构建,将产生每个方法树的变种并将它们还给我?
class Program
{
static void Main(string[] args)
{
var query = from c in customers
join o in orders on c.Id equals o.CustomerId
select new
{
CustomerId = c.Id,
OrderId = o.Id
};
var plans = new Visitor().GetPlans(query);
}
}
谁能告诉我怎样才能修改Visitor
GetPlans
类的方法来产生这些变化?
编辑 - 是这样的:
class Visitor : ExpressionVisitor
{
private List<Expression> exprs = new List<Expression>();
public List<Expression> GetPlans(Expression expr)
{
Visit(expr);
return exprs;
}
override VisitJoin(JoinExpression join)
{
// For this join, I can return the following (trite example)
// return MergeJoinExpresion
// return NestLoopJoinExpresion
var choices = new Expression[] { MergeJoinExpresion.Create(join), NestLoopJoinExpresion.Create(join) };
foreach(var choice in choices)
{
var cloned = Cloner.Clone(choice);
var newTree = base.VisitJoin(cloned);
exprs.Add(newTree);
}
return base.VisitJoin(join);
}
}
不确定访问者是否是最好的方式。 VisitJoin必须返回它生成的多个变体。所有呼叫者必须支持多种变体,并且他们自己可能会生成多种变体无论如何,加入计划通常不是以这种简单的穷举方式完成的,因为时间复杂性将呈指数级增长。 – usr
这个连接只是我能想到的一个非常简单的例子中最简单的例子。我使用表达式树来生成不同的计划 - 并且需要一种生成每个可能结果的方法(无论它是用于Join还是别的)。所以问题是如何生成不同版本的树,可以在生成期间生成访问节点。这是我可以仔细检查,我选择的查询计划是最好的,通过检查一个详尽的搜索... – Jack
我想你可以使它的工作,如果你让访问者的方法返回IEnumerable而不是表达式。 –
usr