Alternate way of Exists with Having clause - sql

The below query is to retrieve the records whose (SGT_DISBURSEMENT_REQUEST) count is only one. It is working great. But performance wise it didn't took so much of time since we have 100 000 records.
Is there any better way to do it?
SELECT PA.PERSON_ID,
PA.PERSON_ACCOUNT_ID,
DR.DISBURSEMENT_REQUEST_ID,
SUM
(
ISNULL(CD.EE_PRE_TAX_AMT,0) +
ISNULL(CD.EE_ADDL_PRE_TAX_AMT,0)
) AS EE_PRE_TAX_CONTRIB,
DD.PRE_TAX_AMOUNT AS DISB_DTL_PRE_TAX_AMOUNT,
SUM
( ISNULL(CD.EE_POST_TAX_AMT,0) +
ISNULL(CD.EE_ADDL_POST_TAX_AMT,0)
) AS EE_POST_TAX_CONTRIB,
DD.POST_TAX_AMOUNT AS DISB_DTL_POST_TAX_AMOUNT
FROM SGT_DISBURSEMENT_REQUEST DR
INNER JOIN SGT_DISBURSEMENT_DETAIL DD ON DR.DISBURSEMENT_REQUEST_ID = DD.DISBURSEMENT_REQUEST_ID
INNER JOIN SGT_PERSON_ACCOUNT PA ON PA.PERSON_ACCOUNT_ID = DR.PERSON_ACCOUNT_ID
INNER JOIN SGT_CONTRIB_DTL CD ON CD.PERSON_ACCOUNT_ID = PA.PERSON_ACCOUNT_ID
WHERE DR.REQUEST_CATEGORY_VALUE = 'REFD' AND
DD.DETAIL_CATEGORY_VALUE = 'REFD' AND
CD.STATUS_VALUE = 'VALD' AND
CD.POSTED_DATE <= DR.ACCEPTED_TO_PAYROLL_DATE AND
DR.DISBURSEMENT_STATUS_VALUE <> 'CANL' AND
EXISTS
(
SELECT 1 FROM SGT_DISBURSEMENT_REQUEST SDR1
INNER JOIN SGT_DISBURSEMENT_DETAIL SDD1 ON SDD1.DISBURSEMENT_REQUEST_ID = SDR1.DISBURSEMENT_REQUEST_ID
WHERE SDR1.PERSON_ACCOUNT_ID = DR.PERSON_ACCOUNT_ID and SDD1.DETAIL_CATEGORY_VALUE = DD.DETAIL_CATEGORY_VALUE
GROUP BY SDR1.PERSON_ACCOUNT_ID
HAVING COUNT(*) = 1
)
GROUP BY
PA.PERSON_ID, PA.PERSON_ACCOUNT_ID, DR.DISBURSEMENT_REQUEST_ID, DD.PRE_TAX_AMOUNT, DD.POST_TAX_AMOUNT

For Performance improvement you can have the Filter Conditions in the Join Clause, instead of where clause. In this case each join will restrict the results to even lesser number of rows.
SELECT PA.PERSON_ID,
PA.PERSON_ACCOUNT_ID,
DR.DISBURSEMENT_REQUEST_ID,
SUM
(
ISNULL(CD.EE_PRE_TAX_AMT,0) +
ISNULL(CD.EE_ADDL_PRE_TAX_AMT,0)
) AS EE_PRE_TAX_CONTRIB,
DD.PRE_TAX_AMOUNT AS DISB_DTL_PRE_TAX_AMOUNT,
SUM
( ISNULL(CD.EE_POST_TAX_AMT,0) +
ISNULL(CD.EE_ADDL_POST_TAX_AMT,0)
) AS EE_POST_TAX_CONTRIB,
DD.POST_TAX_AMOUNT AS DISB_DTL_POST_TAX_AMOUNT
FROM SGT_DISBURSEMENT_REQUEST DR
INNER JOIN SGT_DISBURSEMENT_DETAIL DD ON DR.DISBURSEMENT_REQUEST_ID = DD.DISBURSEMENT_REQUEST_ID AND DR.REQUEST_CATEGORY_VALUE = 'REFD' AND
DD.DETAIL_CATEGORY_VALUE = 'REFD' AND DR.DISBURSEMENT_STATUS_VALUE <> 'CANL'
INNER JOIN SGT_PERSON_ACCOUNT PA ON PA.PERSON_ACCOUNT_ID = DR.PERSON_ACCOUNT_ID
INNER JOIN SGT_CONTRIB_DTL CD ON CD.PERSON_ACCOUNT_ID = PA.PERSON_ACCOUNT_ID AND CD.STATUS_VALUE = 'VALD' AND
CD.POSTED_DATE <= DR.ACCEPTED_TO_PAYROLL_DATE
WHERE EXISTS
(
SELECT 1 FROM SGT_DISBURSEMENT_REQUEST SDR1
INNER JOIN SGT_DISBURSEMENT_DETAIL SDD1 ON SDD1.DISBURSEMENT_REQUEST_ID = SDR1.DISBURSEMENT_REQUEST_ID
WHERE SDR1.PERSON_ACCOUNT_ID = DR.PERSON_ACCOUNT_ID and SDD1.DETAIL_CATEGORY_VALUE = DD.DETAIL_CATEGORY_VALUE
GROUP BY SDR1.PERSON_ACCOUNT_ID
HAVING COUNT(*) = 1
)
GROUP BY
PA.PERSON_ID, PA.PERSON_ACCOUNT_ID, DR.DISBURSEMENT_REQUEST_ID, DD.PRE_TAX_AMOUNT, DD.POST_TAX_AMOUNT

if you use count over it should give you the same result and be better for performance:
select * from (
SELECT PA.PERSON_ID,
PA.PERSON_ACCOUNT_ID,
DR.DISBURSEMENT_REQUEST_ID,
SUM
(
ISNULL(CD.EE_PRE_TAX_AMT,0) +
ISNULL(CD.EE_ADDL_PRE_TAX_AMT,0)
) AS EE_PRE_TAX_CONTRIB,
DD.PRE_TAX_AMOUNT AS DISB_DTL_PRE_TAX_AMOUNT,
SUM
( ISNULL(CD.EE_POST_TAX_AMT,0) +
ISNULL(CD.EE_ADDL_POST_TAX_AMT,0)
) AS EE_POST_TAX_CONTRIB,
DD.POST_TAX_AMOUNT AS DISB_DTL_POST_TAX_AMOUNT
,count(*) over(partition by PA.PERSON_ACCOUNT_ID ) as ct
FROM SGT_DISBURSEMENT_REQUEST DR
INNER JOIN SGT_DISBURSEMENT_DETAIL DD ON DR.DISBURSEMENT_REQUEST_ID = DD.DISBURSEMENT_REQUEST_ID
INNER JOIN SGT_PERSON_ACCOUNT PA ON PA.PERSON_ACCOUNT_ID = DR.PERSON_ACCOUNT_ID
INNER JOIN SGT_CONTRIB_DTL CD ON CD.PERSON_ACCOUNT_ID = PA.PERSON_ACCOUNT_ID
WHERE DR.REQUEST_CATEGORY_VALUE = 'REFD' AND
DD.DETAIL_CATEGORY_VALUE = 'REFD' AND
CD.STATUS_VALUE = 'VALD' AND
CD.POSTED_DATE <= DR.ACCEPTED_TO_PAYROLL_DATE AND
DR.DISBURSEMENT_STATUS_VALUE <> 'CANL'
GROUP BY
PA.PERSON_ID, PA.PERSON_ACCOUNT_ID, DR.DISBURSEMENT_REQUEST_ID, DD.PRE_TAX_AMOUNT, DD.PRE_TAX_AMOUNT, DD.POST_TAX_AMOUNT
) as x
where x.ct = 1

I would materialize this to a #temp
SELECT DR1.PERSON_ACCOUNT_ID, SDD1.DETAIL_CATEGORY_VALUE
FROM SGT_DISBURSEMENT_REQUEST SDR1
JOIN SGT_DISBURSEMENT_DETAIL SDD1
ON SDD1.DISBURSEMENT_REQUEST_ID = SDR1.DISBURSEMENT_REQUEST_ID
GROUP BY SDR1.PERSON_ACCOUNT_ID, SDD1.DETAIL_CATEGORY_VALUE
HAVING COUNT(*) = 1
join #temp
on #temp.PERSON_ACCOUNT_ID = DR.PERSON_ACCOUNT_ID
and #temp.DETAIL_CATEGORY_VALUE = DD.DETAIL_CATEGORY_VALUE

Related

SQL Server rows count return with inner join more than once in same table

I have this code
select
count(cat_item_tb.item_id),
count(t.item_id)
from
cat_tb
inner join
item_tb on cat_tb.cat_id = item_tb.cat_id
inner join
cat_item_tb on item_tb.item_id = cat_item_tb.item_id and t.ss = 0
inner join
cat_item_tb t on item_tb.item_id = t.item_id and t.ss = 1
All I need to return value without duplicate. In past code it must return 7 in every count but it's return 49 the two count() affected each other. I use distinct but it does not return the correct count, because there is (item_id) more than once in table
Thank you so much
Seems to me that you need to count distinct, to count the unique values
...
count(DISTINCT cat_item_tb.item_id)
...
The 2 counts in your query will be the same. That's simply because you INNER JOIN on those item_id's. So they will be identical by defenition.
If I understand correctly the task, you could use groub by to get desired results
select count(cat_item_tb.item_id), cat_item_tb.ss
from cat_tb
inner join item_tb on cat_tb.cat_id = item_tb.cat_id
inner join cat_item_tb on item_tb.item_id = cat_item_tb.item_id
where t.ss = 0 or t.ss = 1
group by cat_item_tb.ss
The query will return 2 rows with count values in first column.
More info about group by is here link
I believe you wanted to write something like:
select
count(c1.item_id),
count(c2.item_id)
from
cat_tb as a
inner join item_tb as b on ( a.cat_id = b.cat_id )
inner join cat_item_tb as c1 on ( b.item_id = c1.item_id ) and ( c1.ss = 0 )
inner join cat_item_tb as c2 on ( b.item_id = c2.item_id ) and ( c2.ss = 1 )
This won't work because of the inner joins. If the first join of c1 returns 3 rows and the second join of c2 returns 4 rows, you end up with count = 3*4
Try this:
;with cte1 as (
select
b.item_id,
'c1_count' = count( c1.item_id )
from
item_tb as b
left join cat_item_tb as c1 on ( b.item_id = c1.item_id ) and ( c1.ss = 0 )
group by
b.item_id
),
cte2 as (
select
b.item_id,
'c2_count' = count(c2.item_id)
from
item_tb as b
left join cat_item_tb as c2 on ( b.item_id = c2.item_id ) and ( c2.ss = 1 )
group by
b.item_id
)
select
a.item_id, a.c1_count, b.c2_count
from
cte1 as a
inner join cte2 as b on ( b.item_id = a.item_id )
For performance reasons you could replace the left join in ctes with inner join ONLY if you are sure that there are rows in cat_item_tb with item_id and all of them has the ss column set to 0 or 1.

Group BY Expression column

we're trying to make our table add together all values in column 2 (QtyComp - an expression column of qtyorder * totalqty basically), where they have the same ItemNo (column 1).
So, we currently get the below:
ItemNo QtyCom
7441 3
7441 1
7441 5
What we want is it to return this:
ItemNo QtyCom
7441 9
Our code is below; I've bolded the part that we need it to sum the results of:
SELECT TOP (100) PERCENT ItemSpecs_2.itemno,
workorderdetails.qtycomplete *
ItemSpecFullStruc_2.totalqtyperroot AS QtyComp
FROM dbo.workorderdetails AS WorkOrderDetails
INNER JOIN dbo.itemspecfullstruc AS ItemSpecFullStruc_2
ON ItemSpecFullStruc_2.rootitemspecid =
workorderdetails.itemspecid
INNER JOIN dbo.itemspecs AS ItemSpecs_2
ON ItemSpecs_2.itemspecid = ItemSpecFullStruc_2.childitemspecid
INNER JOIN dbo.workorder AS WorkOrder_1
ON WorkOrder_1.workorderid = workorderdetails.workorderid
LEFT OUTER JOIN dbo.tobescheduled_completed
ON WorkOrder_1.workorderid =
dbo.tobescheduled_completed.workorderid
WHERE ( workorderdetails.completed = 1 )
AND ( workorderdetails.compdate > Getdate() - 42 )
GROUP BY ItemSpecs_2.itemno,
workorderdetails.qtyordered,
ItemSpecFullStruc_2.totalqtyperroot,
workorderdetails.[lineno],
workorderdetails.qtycomplete,
workorderdetails.compdate,
workorderdetails.qtycomplete * ItemSpecFullStruc_2.totalqtyperroot
We would really appreciate some ideas!
Thanks,
Trish
Try this
SELECT TOP (100) PERCENT ItemSpecs_2.itemno,
sum(workorderdetails.qtycomplete *
ItemSpecFullStruc_2.totalqtyperroot) AS QtyComp
FROM dbo.workorderdetails AS WorkOrderDetails
INNER JOIN dbo.itemspecfullstruc AS ItemSpecFullStruc_2
ON ItemSpecFullStruc_2.rootitemspecid =
workorderdetails.itemspecid
INNER JOIN dbo.itemspecs AS ItemSpecs_2
ON ItemSpecs_2.itemspecid = ItemSpecFullStruc_2.childitemspecid
INNER JOIN dbo.workorder AS WorkOrder_1
ON WorkOrder_1.workorderid = workorderdetails.workorderid
LEFT OUTER JOIN dbo.tobescheduled_completed
ON WorkOrder_1.workorderid =
dbo.tobescheduled_completed.workorderid
WHERE ( workorderdetails.completed = 1 )
AND ( workorderdetails.compdate > Getdate() - 42 )
GROUP BY ItemSpecs_2.itemno,
workorderdetails.qtyordered,
ItemSpecFullStruc_2.totalqtyperroot,
workorderdetails.[lineno],
workorderdetails.qtycomplete,
workorderdetails.compdate
Once you will use top for select statement, you need to use order by. you can try the following query.
SELECT TOP(100) PERCENT A.itemno,SUM(QtyComp) FROM
(SELECT ItemSpecs_2.itemno,
(workorderdetails.qtycomplete *
ItemSpecFullStruc_2.totalqtyperroot) AS QtyComp
FROM dbo.workorderdetails AS WorkOrderDetails
INNER JOIN dbo.itemspecfullstruc AS ItemSpecFullStruc_2
ON ItemSpecFullStruc_2.rootitemspecid =
workorderdetails.itemspecid
INNER JOIN dbo.itemspecs AS ItemSpecs_2
ON ItemSpecs_2.itemspecid = ItemSpecFullStruc_2.childitemspecid
INNER JOIN dbo.workorder AS WorkOrder_1
ON WorkOrder_1.workorderid = workorderdetails.workorderid
LEFT OUTER JOIN dbo.tobescheduled_completed
ON WorkOrder_1.workorderid =
dbo.tobescheduled_completed.workorderid
WHERE ( workorderdetails.completed = 1 )
AND ( workorderdetails.compdate > Getdate() - 42 ) ) A
GROUP BY A.itemno
ORDER BY A.itemno
Thanks
SELECT SUM(QTYCOM) OVER (PARTITION BY ITEMNO)
FROM
...

Sum of resulting set of rows in SQL

I've got the following query:
SELECT DISTINCT CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
/*MC.chemical_percentage,*/
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
--AND CU.permit_id = 2118
--GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month, MC.chemical_id, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound
--ORDER BY C.chemical_name ASC
Which returns:
But what I need is to return one row per month per material adding up the values of POC per month and NON_POC per month.
So, I should end up with something like:
Month material_id material_name POC NON_POC
1 52 Krylon... 0.107581 0.074108687
2 52 Krylon... 0.143437 0.0988125
I tried using SUM but it sums up the same result multiple times:
SELECT /*DISTINCT*/ CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
--MC.chemical_percentage,
POC_emissions = SUM(
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END),
NON_POC_emissions = SUM(
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END)
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE M.material_id = 52
--AND CU.permit_id = 187
AND (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month/*, CU.year, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound*/
--ORDER BY C.chemical_name ASC
The first query has a DISTINCT clause. What is the output without the DISTINCT clause. I suspect you have more rows than shows in your screenshot.
Regardless, you could try something like this to get the desired result.
select permit_id, month, material_id, material_name,
sum(poc_emissions), sum(non_poc_emissions)
from (
SELECT DISTINCT CU.permit_id, CU.month, M.material_id, M.material_name,
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
) main
group by permit_id, month, material_id, material_name
Explanation
Since the results you retrieved by doing a DISTINCT was consider source-of-truth, I created an in-memory table by making it a sub-query. However, this subquery must have a name of some kind...whatever name. I gave it a name main. Subqueries look like this:
select ... from (sub-query) <give-it-a-table-name>
Simple Example:
select * from (select userid, username from user) user_temp
Advanced Example:
select * from (select userid, username from user) user_temp
inner join (select userid, sum(debits) as totaldebits from debittable) debit
on debit.userid = user_temp.userid
Notice how user_temp alias for the subquery can be used as if the sub-query was a real table.
Use above query in subquery and group by (month) and select sum(POC_emissions) and sum(NON_POC_emissions )

Select count from subselect syntax

I have two tables SurrogateKeys and SurrogateKeyRelatives.
I will find out of there are records that satisfy a query joining both tables.
I am playing with two options:
select count(*) from
(
select top(1) sk.Username from SurrogateKeys as sk
inner join SurrogateKeyRelatives as skr
on sk.Id = skr.SurrKeyId
where
sk.Username='John.Doe' and
skr.SomeField = 1
)
and
if exists(
select top(1) sk.Username from SurrogateKeys as sk
inner join SurrogateKeyRelatives as skr
on sk.Id = skr.SurrKeyId
where
sk.Username='John.Doe' and
skr.SomeField = 1
)
begin
select 1
end
else
begin
select 0
end
Attempting option 1 gives me Incorrect Syntax near ')'. What am I missing in the above query?
select count(*) from
(
select top(1) sk.Username from SurrogateKeys as sk
inner join SurrogateKeyRelatives as skr
on sk.Id = skr.SurrKeyId
where
sk.Username='John.Doe' and
skr.SomeField = 1
) Q1
select top(1)
CASE
WHEN skr.SurrKeyId is null THEN 0
ELSE 1
END
from SurrogateKeys as sk
left join SurrogateKeyRelatives as skr
on sk.Id = skr.SurrKeyId
and sk.Username='John.Doe'
and skr.SomeField = 1
in first option try to give inner query a table name after brace
select count(*) from
(
select top(1) sk.Username from SurrogateKeys as sk
inner join SurrogateKeyRelatives as skr
on sk.Id = skr.SurrKeyId
where
sk.Username='John.Doe' and
skr.SomeField = 1
) table1

How to convert correlated sub query containing duplicate table to non-correlated one?

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 ?
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.