如何将包含重复表的相关子查询转换为非相关表?

时间:2014-01-30 12:10:37

标签: sql database performance subquery informix

我必须将相关的子查询转换为性能问题的非相关子查询。

像那样:

相关子查询:(太慢)返回4000行

SELECT a.personid,a.name,b.conid,d.condat,e.connam 
FROM main_empr a INNER JOIN coninr b 
ON a.personid = b.personid AND a.calc_year = b.calc_year 
INNER JOIN mainconinr c 
ON b.conid = c.conid 
INNER JOIN coninr d 
ON a.personid = d.personid AND a.calc_year = d.calc_year 
INNER JOIN mainconinr e 
ON d.conid = e.conid  
WHERE c.active_flag = 1 and c.endreward_flag = 1 
 AND d.condat = (SELECT MIN(bb.condat) FROM coninr bb WHERE bb.personid = b.personid AND bb.calc_year = b.calc_year AND ((bb.conid > 0  AND bb.conid  < 4 ) OR (bb.conid IN(16,6) )) )
 AND b.condat = (SELECT MAX(bb.condat) FROM coninr bb WHERE bb.personid = b.personid AND bb.calc_year = b.calc_year AND ((bb.conid > 0  AND bb.conid  < 4 ) OR (bb.conid IN(16,6) )) )  
 AND ( 0 = ( SELECT COUNT(*) FROM servmain x WHERE x.personid = a.personid AND x.calc_year = a.calc_year ) 
 OR b.condat > ( SELECT MAX(x.serv_date) FROM servmain x WHERE x.personid = a.personid  AND x.calc_year = a.calc_year ) ) 
 AND a.calc_year = 2018 

非相关查询:返回约12300行 !!

SELECT a.personid,a.name,b.conid,d.condat,e.connam  
FROM main_empr a INNER JOIN 
coninr b  
ON a.personid = b.personid AND a.calc_year = b.calc_year  
INNER JOIN mainconinr c  
ON b.conid = c.conid  
INNER JOIN coninr d  
ON a.personid = d.personid AND a.calc_year = d.calc_year  
INNER JOIN mainconinr e  ON d.conid = e.conid   
INNER JOIN 
(SELECT MAX(bb.condat) AS condat ,bb.personid,bb.calc_year ,bb.conid 
FROM coninr bb 
 GROUP BY bb.personid,bb.calc_year,bb.conid  
)Max_cont  
ON Max_cont.personid = b.personid AND Max_cont.calc_year = b.calc_year AND Max_cont.condat = b.condat AND ((Max_cont.conid > 0  AND Max_cont.conid  < 4 ) OR (Max_cont.conid IN(16,6) )) 

INNER JOIN 
(SELECT MIN(dd.condat) AS condat ,dd.personid,dd.calc_year,dd.conid  
FROM coninr dd  GROUP BY dd.personid,dd.calc_year,dd.conid  
)Min_cont  
ON Min_cont.personid = d.personid AND Min_cont.calc_year = d.calc_year AND Min_cont.condat = d.condat AND ((Min_cont.conid > 0  AND Min_cont.conid  < 4 ) OR (Min_cont.conid IN(16,6) ))  

WHERE c.active_flag = 1 and c.endreward_flag = 1  
AND ( 0 = ( SELECT COUNT(*) FROM servmain x WHERE x.personid = a.personid AND x.calc_year = a.calc_year )  
OR b.condat > ( SELECT MAX(x.serv_date) FROM servmain x WHERE x.personid = a.personid  AND x.calc_year = a.calc_year ) ) 
 AND a.calc_year = 2018

问题是:

我两次使用coninr表来获取同一行中的最后一个和第一个合同日期。

它在第一个查询中工作正常,但由于相关的子查询,它很慢,但在第二个查询中,它为同一个人带来了多个行,其中一行是第一个合同日期,另一个是最后一个!!

如何解决这个问题?

1 个答案:

答案 0 :(得分:2)

这看起来很合理,但我无法知道它的表现如何:

SELECT a.personid,a.name,b.conid,d.condat,e.connam 
FROM main_empr a INNER JOIN coninr b 
ON a.personid = b.personid AND a.calc_year = b.calc_year 
INNER JOIN mainconinr c 
ON b.conid = c.conid 
INNER JOIN coninr d 
ON a.personid = d.personid AND a.calc_year = d.calc_year 
INNER JOIN mainconinr e 
ON d.conid = e.conid  
inner join
(
    SELECT bb.personid, bb.calc_year, bb.conid, MIN(bb.condat) MinDate, MAX(bb.condat) MaxDate 
    FROM coninr bb WHERE
    where (bb.conid > 0 and bb.conid < 4) or (bb.conid in (6, 16))
    group by bb.personid, bb.calc_year, bb.conid
) zz on d.concat = zz.MinDate and b.condat = zz.MaxDate and b.personid = zz.personid and b.calc_year = zz.calc_year
left outer join
(
    select s.personid, s.calc_year, max(s.serv_date) MaxServDate
    from servmain s
    group by s.personid, s.calc_year
) s on a.personid = s.personid and a.calc_year = s.calc_year
WHERE c.active_flag = 1 and c.endreward_flag = 1 
 and (s.MaxServDate is null or b.condat ? s.MaxServDate
 AND a.calc_year = 2018 

您不需要对表coninr进行两次查询,您可以在group by的同一查询中获得min和max。此外,对于ServMain,执行左外连接并放入其中为null(相当于count(*)= 0)或小于b.condat的位置来处理。