SQL server alternative of OR in where condition - sql

SELECT DISTINCT c.ID FROM tbl_Case c INNER JOIN
tbl_RequestBaseRequest b ON CaseId = c.ID
WHERE AreCalculationsCompleted = 0
AND b.IsApplicantRequest = 1
and c.IsArchived=0
AND (b.ID IN (SELECT DISTINCT ClientRequestId FROM tbl_Response)
OR b.OldClientRequestId IN (SELECT DISTINCT ClientRequestId FROM tbl_Response))
What should be the alternative of OR, this OR is making this query
really slow.

SELECT DISTINCT c.ID FROM tbl_Case c
INNER JOIN tbl_RequestBaseRequest b ON CaseId = c.ID
WHERE AreCalculationsCompleted = 0
AND b.IsApplicantRequest = 1
and c.IsArchived=0
AND exists (SELECT 1 FROM tbl_Response t
WHERE t.ClientRequestId = b.ID OR t.ClientRequestId = b.OldClientRequestId
)

You might try removing the distinct and being sure you have an index on tbl_Response(ClientRequestId):
SELECT DISTINCT c.ID
FROM tbl_Case c INNER JOIN
tbl_RequestBaseRequest b
ON CaseId = c.ID
WHERE AreCalculationsCompleted = 0 AND
b.IsApplicantRequest = 1 and
c.IsArchived = 0 AND
(b.ID IN (SELECT ClientRequestId FROM tbl_Response) OR
b.OldClientRequestId IN (SELECT ClientRequestId FROM tbl_Response)
);
Other indexes might help. Also, removing the outer DISTINCT (if it is not necessary will also boost performance). Other indexes might help, but it is not possible to specify because you haven't qualified AreCalculationsCompleted.

SELECT DISTINCT c.id
FROM tbl_Case c
JOIN tbl_RequestBaseRequest b ON CaseId = c.id
WHERE AreCalculationsCompleted = 0
AND b.IsApplicantRequest = 1
AND c.IsArchived = 0
AND EXISTS(
SELECT *
FROM tbl_Response r
WHERE r.ClientRequestId IN (b.id, b.OldClientRequestId)
)

SELECT DISTINCT c.ID
FROM tbl_Case c
INNER JOIN tbl_RequestBaseRequest b ON CaseId = c.ID
INNER JOIN tbl_Response r ON (b.ID = r.ClientRequestId OR b.OldClientRequestId = r.ClientRequestId)

Often rewriting OR as UNION helps.
SELECT c.ID
FROM tbl_Case c
INNER JOIN tbl_RequestBaseRequest b
ON CaseId = c.ID
WHERE AreCalculationsCompleted = 0
AND b.IsApplicantRequest = 1
AND c.IsArchived = 0
AND b.ID IN (SELECT ClientRequestId
FROM tbl_Response)
UNION
SELECT c.ID
FROM tbl_Case c
INNER JOIN tbl_RequestBaseRequest b
ON CaseId = c.ID
WHERE AreCalculationsCompleted = 0
AND b.IsApplicantRequest = 1
AND c.IsArchived = 0
AND b.OldClientRequestId IN (SELECT ClientRequestId
FROM tbl_Response)
You could tidy this up somewhat by encapsulating the join of c and b into a CTE and referencing that in both branches of the UNION instead of repeating it - or materialising into a temp table if that initial join is itself expensive.

SELECT DISTINCT c.ID
FROM tbl_Case c
INNER JOIN tbl_RequestBaseRequest b
ON CaseId = c.ID
AND AreCalculationsCompleted = 0
AND b.IsApplicantRequest = 1
AND c.IsArchived=0
AND EXISTS (SELECT NULL
FROM tbl_Response
WHERE ClientRequestId IN (b.ID, b.OldClientRequestId))

Related

How can I count the same fields

I'm making a query to get the stages of a case. So Now I have three cases with 3 stages (the last stage inserted in the table user_case_stage).
SELECT DISTINCT ON (c.id)
c.id,
f.name
FROM schema.user a
JOIN schema.intern_user b ON a.id = b."userId"
JOIN schema.user_case c ON b.id = c."internUserId"
JOIN schema.user_case_stage d ON c.id = d."userCaseId"
JOIN schema.stage f ON d."stageId" = f.id
WHERE b.id = 1
ORDER BY c.id,d."createdAt" DESC
Result:
caseId stageName
1 "Pasive"
6 "Closed"
7 "Closed"
But I want something to count by stageName like this:
total stageName
1 "Pasive"
2 "Closed"
assumed your logic is correct , since you didn't provide any information, here is how you can do it:
SELECT
f.name
, count(distinct c.id) total
FROM schema.user a
JOIN schema.intern_user b ON a.id = b."userId"
JOIN schema.user_case c ON b.id = c."internUserId"
JOIN schema.user_case_stage d ON c.id = d."userCaseId"
JOIN schema.stage f ON d."stageId" = f.id
WHERE b.id = 1
group by f.name

Sql query if is null

I need help to return a result if value exists or no.
UPDATED: The image show where I need help:
You can do it using CASE EXPRESSION with a LEFT JOIN .
I didn't fully understand the output you expect, but just add the columns you want:
#in_myvar = 11
select bt.username,at.postid,
CASE WHEN ct.userid is null the 0 else 1 end as c_ind
from A at
INNER JOIN B bt
ON (at.userid = bt.userid and bt.userid = #in_myvar)
LEFT JOIN C ct
ON(ct.userid = at.userid)
Use this query:
declare #in_myvar int = 11
select B.username,A.postid,(Case when C.postid = A.postid then 1 Else 0 end) as UserExists
from A inner join B on A.userID = B.UserID
Left Join C
On C.userID = A.userID
where B.userID = #in_myvar
You want a result row for each record in A. So select from A. The data from the other tables can be got with subqueries:
select
(select username from b where b.userid = a.userid) as username,
a.postid,
case when exists (select * from c where c.userid = a.userid) then 1 else 0 end as has_c
from a;
As B:A = 1:n, you can also join B, if you like that better:
select
b.username,
a.postid,
case when exists (select * from c where c.userid = a.userid) then 1 else 0 end as has_c
from a
join b on b.userid = a.userid;

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.

Doing an "if" type statement in a sql where clause

I'm trying to do some sort of "if" statement in my where clause. I realize that sql doesn't support this but I'm sure there must be some way to make this work with sql syntax. As shown in the area I have in bold I'm trying to find all items that begin with d and filter them out if their userfld2 also = container.
Is there a more reasonable way to do this than I am doing or am I way off the mark?
Thanks in advance.
Select a.ItemID
, b.ConversionFactor VCaseAmt
, sum(c.ConversionFactor + 1) SCaseAmt
, a.status
, a.UserFld2
From timItem a
inner join timItemUnitOfMeas b on a.ItemKey = b.ItemKey
and b.TargetUnitMeasKey = 115
left join timItemUnitOfMeas c on a.ItemKey = c.ItemKey
and c.TargetUnitMeasKey = 116
left join timItemUnitOfMeas d on a.ItemKey = d.ItemKey
and d.TargetUnitMeasKey = 126
Where d.TargetUnitMeasKey is null
and b.ConversionFactor != c.ConversionFactor + 1
and a.Status = 1
and **(filter a.itemid not like 'd%' when a.userfld2 = 'Container')**
Group by a.ItemID, b.TargetUnitMeasKey, b.ConversionFactor, C.TargetUnitMeasKey
, c.ConversionFactor, a.status, a.UserFld2
Order by a.ItemID
Use this:
Select a.ItemID, b.ConversionFactor VCaseAmt, sum(c.ConversionFactor + 1) SCaseAmt, a.status, a.UserFld2
From timItem a inner join
timItemUnitOfMeas b on a.ItemKey = b.ItemKey and b.TargetUnitMeasKey = 115 left join
timItemUnitOfMeas c on a.ItemKey = c.ItemKey and c.TargetUnitMeasKey = 116 left join
timItemUnitOfMeas d on a.ItemKey = d.ItemKey and d.TargetUnitMeasKey = 126
Where d.TargetUnitMeasKey is null and b.ConversionFactor != c.ConversionFactor + 1 and a.Status = 1 and
not (a.itemid like 'd%' AND a.userfld2 = 'Container')
Group by a.ItemID, b.TargetUnitMeasKey, b.ConversionFactor, C.TargetUnitMeasKey, c.ConversionFactor, a.status, a.UserFld2
Order by a.ItemID

Conditional INNER JOIN in SQL Server

I have a rather complex query that pretty much mimics a test query I have below:
SELECT C.*
FROM Customer C
INNER JOIN CustDetail CD ON C.CustomerId = CD.CustomerId
INNER JOIN Address A ON CD.DetailID = A.DetailID
INNER JOIN Group G ON C.CustomerId = G.CustomerId --Join only when C.code = 1
INNER JOIN GroupDetail D ON G.GroupId = D.DetailId --Join only when C.code = 1
WHERE G.Active = 1 AND --Only when C.code = 1
D.code = '1' AND --Only when C.code = 1
C.Id = #customerId
I'd like to do INNER JOINs on Group G and GroupDetail D (and ofcourse not have them in the WHERE conditions based on the table column C.code = 1
I replaced the INNER JOINs with LEFT OUTER JOINs for both the join conditions, but the result set is not what was expected
How do I conditionally do the JOIN
SELECT C.*
FROM Customer C
INNER JOIN CustDetail CD ON C.CustomerId = CD.CustomerId
INNER JOIN Address A ON CD.DetailID = A.DetailID
LEFT OUTER JOIN Group G ON C.CustomerId = G.CustomerId
LEFT OUTER JOIN GroupDetail D ON G.GroupId = D.DetailId
WHERE ((G.Active = 1 AND C.code = 1) OR G.Active IS NULL) AND
((D.code = '1' AND C.code = 1) OR D.code IS NULL) AND
C.Id = #customerId
I'm guessing you didn't include the IS NULL checks before so you never got to see rows where C.code <> 1 ?
You should check for NULL on a field that will never be null. This is almost always 'id', but it's not clear that you have a G.id or a D.id.
I'm guessing what you want is just a tighter ON clause, and a compound condition.
SELECT C.*
FROM Customer C
INNER JOIN CustDetail CD ON C.CustomerId = CD.CustomerId
INNER JOIN Address A ON CD.DetailID = A.DetailID
-- the next two joins happen only when c.code=1
-- their columns will be null when there is no match.
LEFT JOIN Group G ON C.CustomerId = G.CustomerId AND C.Code = 1
LEFT JOIN GroupDetail D ON G.GroupId = D.DetailId AND C.Code = 1
WHERE C.Id = #customerId AND --always check this
-- this condition is true if code is null or code isn't 1,
((C.code IS NULL or C.code <> 1)
-- or (if the code is 1), it is true if g.active and d.code
OR (G.Active = 1 AND D.code = '1'))
This will do a semi-join only when code is 1.
SELECT C.*
FROM Customer C
INNER JOIN CustDetail CD
ON C.CustomerId = CD.CustomerId
INNER JOIN Address A
ON CD.DetailID = A.DetailID
WHERE
C.Id = #customerId AND
(c.code != 1 OR
EXISTS(
SELECT NULL
FROM Group G
JOIN GroupDetail D ON G.GroupId = D.DetailId
WHERE
C.CustomerId = G.CustomerId AND
G.Active = 1 AND
D.code = '1'
))