Question

I have to convert the correlated sub-query to non-correlated sub-query cuz of performance issues .

like that :

The correlated sub-query :(So slow ) returns 4000 row

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 

The non-correlated query :returns about 12300 rows!!

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

The problem is :

I use the coninr table twice to get the last and the first contract date in the same row .

It works fine in the first query but it was so slow because of the correlated sub-query,but in the second query it brings more than one row for the same person one of them for the first contract date and the other for the last one !!

How to fix this problem ?

Was it helpful?

Solution

This looks reasonable, but I've no way to know how it'll perform:

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 

You don't need two queries for table coninr, you can get min and max in the same query with the group by. Also, for ServMain, doing a left outer join and putting in the where that either it's null (equivalent to count(*) = 0) or is less than b.condat takes care of that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top