LINQ:构建 IQueryable 提供程序 - 第 九 部分:删除冗余子查询

英文原文是Matt Warren发表在MSDN Blogs的系列文章之一,英文渣渣,翻译不供参考,请直接看原文

现在写一篇新的文章的时间变得越来越长,似乎已经成了一个趋势了。要怪就怪电视编剧罢工吧,嗯。

Cleaning up the Mess

我之前说过要把我们的查询翻译器不断累积下来的不必要的嵌套select表达式给清理掉。对于人类的大脑来说,简化一条SQL是一件很简单的事情。但是,对于计算机程序而言,保留这些无用的嵌套查询却更加容易,毕竟它们的语义是一样的。再者,我们希望少写一点代码的心情也无可厚非。

我们很容易就能从一条带有where子句的简单的查询中看出问题所在。

    from c in db.Customers
    where c.Country == "UK"
    select c;

这条普通的查询将翻译为下面的SQL:

	SELECT t1.Country, t1.CustomerID, t1.ContactName, t1.Phone, t1.City
	FROM (
	  SELECT t0.Country, t0.CustomerID, t0.ContactName, t0.Phone, t0.City	  FROM Customers AS t0
	) AS t1
	WHERE (t1.Country = 'UK')

为什么会有一个多余的SELECT?如果你理解了我们的翻译器的工作方式,并且知道这条LINQ查询的本质是什么的话,很容易就能知道答案。

这条LINQ查询的方法调用语法如下:

    db.Customers.Where(c => c.Country == "UK").Select(c => c);

这里面有两个LINQ查询操作符,Where()Select()。我们在QueryBinder类中的翻译引擎将这两个方法调用翻译为两个独立的SelectExpression

理想情况下,SQL查询应该如下所示:

	SELECT t0.Country, t0.CustomerID, t0.ContactName, t0.Phone, t0.City
	FROM Customers AS t0
	WHERE (t0.Country = 'UK')

然而,这只是很简单的情况,随着操作符的增加,所生成的SQL会越来越槽糕。你觉得翻译器能够聪明到将多个where子句合并到一起吗?我确实没有添加任何代码。如果语言编译器能够帮我们完成这个工作就再好不过了,但是如果额外的where子句是在原查询已经创建完成之后添加到其中的又会如何呢?

	var query = 
	    from c in db.Customers
	    where c.Country == "UK"
	    select c;
	// ...
	query = from c in query
	        where c.Phone == "555-5555"
	        select c;

这样翻译出来的SQL就变成了一个三层的庞然大物,可它又不能吃,要那么大干嘛。

	SELECT t2.CustomerID, t2.ContactName, t2.Phone, t2.City, t2.Country
	FROM (
	  SELECT t1.CustomerID, t1.ContactName, t1.Phone, t1.City, t1.Country
	  FROM (
	    SELECT t0.CustomerID, t0.ContactName, t0.Phone, t0.City, t0.Country
	    FROM Customers AS t0
	  ) AS t1
	  WHERE (t1.Country = 'UK')
	) AS t2
	WHERE (t2.Phone = '555-5555')

不仅如此,我只是添加了一个小小的投影,翻译器都会额外创建一个嵌套查询。

	var query = 
	    from c in db.Customers
	    where c.Country == "UK"
	    select c.CustomerID;

翻译出来的SQL如下:

	SELECT t2.CustomerID
	FROM (
	  SELECT t1.CustomerID, t1.ContactName, t1.Phone, t1.City, t1.Country
	  FROM (
	    SELECT t0.CustomerID, t0.ContactName, t0.Phone, t0.City, t0.Country
	    FROM Customers AS t0
	  ) AS t1
	  WHERE (t1.Country = 'UK')
	) AS t2

为什么内层的查询要把外层从来没有用到过的数据给select出来?但愿数据库的优化做得够好,不要传输那些用不上或者没有必要返回到客户端的数据。但是,如果我们的查询翻译器能够自己消除这些重复的嵌套,将其转换为像一个真正的人类写出来的简单的形式的话,不是更好吗?这样,我们就可以写像下面一样复杂的查询了:

	var query = from c in db.Customers
	            join o in db.Orders on c.CustomerID equals o.CustomerID
	            let m = c.Phone
	            orderby c.City
	            where c.Country == "UK"
	            where m != "555-5555"
	            select new { c.City, c.ContactName } into x
	            where x.City == "London"
	            select x;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值