难道SQL的子查询就是鸡肋吗?

   MSDN对子查询的定义是这样的:

     可以将一个查询的结果用作另一个查询的输入。可以将子查询的结果用作使用 IN( ) 函数、EXISTS 运算符或 FROM 子句的语句。

     一条好的值得称赞的规则是尽量用连接代替所有的子查询。优化器有时可以自动将子查询“扁平化”,并且用常规或外连接代替。但那样也不总是有效。明确的连接对选择表的顺序和找到最可能的计划给出了更多的选项。当你优化一个特殊查询时,了解一下是否去掉自查询可产生很大的差异。这段话出自http://www.innovatedigital.com/htm_speek/SQLServerOpt.shtml它显然告诉我们在查询的时候最好不要用子查询,当时我并不太在意,至到我有一次遇到了这样的情况。  
      
     现在的网站主要的压力都来自于数据库,频繁的数据库访问经常会使服务器死机。我的原则就是尽量减少数据库的连接,能一次性取出的决不多连接数据库一次,但是有时候并不完全是这样。郁闷,无解。    
 

     基于有的朋友看不明白我写的SQL语句,可能是因为我写的有些字段和本案例没有太大关系的原因,现在特将它们做一个替换.
     

     我的项目里面有两张表:

    1:电影表,里面都是些电影的详细信息.

    字段说明:RESOURCE_VOLUMECOUNT
                它是指一个电影共有多少集.这个字段是int类型的

     其它的和本案例关系不大的字段就省略了.

     2:电影文件表:TB_RESOURCE_PRIMARVIDEO

       它是指电影表里面的具体电影实际存在的集数,例如:功夫有1集,是通过电影ID(resource_id)与电影表关联的.电影表和它是一对多的关系.其它的和本案例关系不大就省略了.

     需求:

          取电影信息记录集,并算出各个电影的缺集情况.例如完整的一总功夫电影应该要有两集,但是电影文件表里面只有一集,那么此时电影缺集为.

  

     下面一条SQL语句里面有一个子查询,info.RESOURCE_VOLUMECOUNT,它是指一个电影共有多少集.这个字段是int类型的.子查询的目的就是计算出一个电影在资源表中总共有多少集(实际存在的), 两者做减法操作就可以计算出这个电影共缺多少集(lastCount)我们知道在这条语句执行的时候,外层记录查询一次在计算 lastCount字段的时候又要查询表:电影表一次.如果最外层有10条记录,那么执行这次查询一共要扫描电影表11次,连接数据库1次.
     

   


string SqlCode = @"select distinct info.resource_officialname ,
          info.resource_id,
          字段1,         
           (
             
info.RESOURCE_VOLUMECOUNT -
               (select count(vid3.resource_id ) from 电影表 info3
                 inner join TB_RESOURCE_PRIMARVIDEO vid3 on
                  info3.resource_id =vid3.resource_id and
                  info.resource_id =vid3.resource_id 
                  
               )
           ) as lastCount,
          字段2
          from 电影表 info
           where 1=1 
";
     
    现在换一种方法来做:

     下面的语句和上面的语句只有一个区别就是没有查询lastCount字段,但是查询出了info.RESOURCE_VOLUMECOUNT字段(完整电影应该有的集数),就是说去掉了那个子查询.在绑定数据的时候,通过每次记录的resource_id(电影ID)重新进数据库执行一次以前的子查询,

    具体实现是这样的:
    在外层记录做循环的时候,通过resource_id来取电影实际存在多少集.在程序中来实现info.RESOURCE_VOLUMECOUNT与"电影实际存在的集数"做减法.这样做也是查询表:电影表总共11次,连接数据库11次.

private string getCount(string resource_id)
{
   
//返回结果
   string s="";
   
string sql=@"select count(vid3.resource_id ) from 电影表 info3
                 inner join TB_RESOURCE_PRIMARVIDEO vid3 on
                  info3.resource_id =vid3.resource_id and
                  info3.resource_id =
"+  resource_id;
   
//执行代码省略
   return s;         
         

}

     下面是去掉了子查询的SQL查询语句。它是最外层的循环,每循环一条记录后用取得的电影ID再调用上面的方法去计算此电影实际有多少集,这样在功能上就与第一种方法(包含子查询)得到的结果是一样的了。但是在效率上有太大的差距。

string SqlCode = @"select distinct info.resource_officialname ,
          info.resource_id,
          字段1,         
            
info.RESOURCE_VOLUMECOUNT,--完整电影应该有多少集
         字段2
          from 电影表 info
           where 1=1 
";

     理论上说应该是查询表11次打开数据库连接1次的在性能上应该会好很多啊,但是实际不则相反,反而是查询表11次打开数据库11次之多的后一种方法在执行时间上会少很多.不知道这样的子查询在实际数据库操作中到底会有多大的实际用处呢,是否真是鸡肋呢?我在实验的时候当数据特别少时差别不大,一旦多了,哪怕只有1000条,性能上都有特别大的差距,有时会出现死机的状况。我用的是interBase下的firebird数据库,这里不能显示出执行的具体时间,抱歉了。虽然是小型的,但是也能反应出问题来,希望高手们看到了能帮我分析一下其中的原因,我们在查询数据的时候是否还能用这样的子查询呢?

     本人的文章发布之后有很多朋友都给出了自己的想法,很多都不错,主要的一种说法就是利用join来替代子查询,可是如果说在取出的字段特别多的时候,进行分组时 group by  中会出现多个字段,这在性能上也未必会好.而且这只是一种解决方案,能不能以我文章中的性能问题分析一下具体原因呢?为什么第二种方案效率会更好呢?

posted on 2008-05-13 11:01  min.jiang  阅读(4913)  评论(30编辑  收藏  举报